RHEL/CentOS/Ubuntu 와 Windows 환경의 PHP 에서 오라클(Oracle) 사용하기

PHP 용 Oracle Driver 인 OCI8/PDO_OCI extension 을 설치하는 방법을 설명.

oracle instant client 12 부터는 9i 를 지원하지 않으므로 9i 에 연결하려면 11 버전을 설치해야 한다.


Windows

  1. 오라클 사이트에 연결하여 인스턴스 클라이언트를 다운받고 압축 해제(Ex: C:\devel\instantclient_12_1)
  2. Windows 의 "고급 시스템 설정"에 들어가서 환경변수 → Path 에 위에서 압축 해제한 경로를 추가
  3. php.ini 에서 다음 항목 주석 해제

    Oracle 12C Instant Client
    extension=oci8_12c
    extension=pdo_oci
  4. 웹 서버 및 PHP 재구동
  5. 제대로 로딩되었는지 phpinfo() 를 통해 확인

    <?php
    phpinfo();

다음과 같은 에러가 난다면 instant client 버전에 맞는 Visual C++ 재배포 패키지를 설치해야 한다.

PHP Warning: PHP Startup: Unable to load dynamic library php_oci8_12c.dll - The specified procedure could not be found.

(지정된 모듈을 찾을 수 없습니다.), php_oci8_12c.dll (지정된 프로시저를 찾을 수 없습니다.)





RHEL/CentOS

CentOS 에 포함된 PHP 는 Oracle driver(OCI8) 가 포함되어 있지 않으므로 PHP 에서 오라클과 연동하려면 드라이버를 직접 빌드해야 한다.


사전 작업

  1. gcc 컴파일러와 개발 도구 설치

    yum groupinstall "Development Tools"
  2. PHP 를 빌드하기 위한 개발 패키지 설치

    WebTatic 저장소에서 PHP 5.5 를 설치했을 경우이며 CentOS 기본 PHP(php) 패키지나 PHP 5.4(php54w) 의 경우 패키지명을 다르게 적어 줘야 한다.

    yum install php55w-pear php55w-devel zlib zlib-devel bc libaio glibc
  1. 패키지를 설치한다.

    yum localinstall oracle-instantclie*rpm
  2. autoconf가 찾을 수 있도록 심볼릭 링크를 생성한다.

    ln -s /usr/include/oracle/12.1/client64/  /usr/include/oracle/12.1/client
    ln -s /usr/lib/oracle/12.1/client64/  /usr/lib/oracle/12.1/client

PDO_OCI 빌드

PDO_OCI 는 안정화 되어 있지 않고 업데이트가 오래된 드라이버이므로 사용하지 않는 게 좋습니다.

대신 OCI8 을 래핑한 https://github.com/yajra/pdo-via-oci8 를 사용하여 PDO 를 에뮬레이션할 수 있습니다.


SELinux 차단 해결

Port

PHP 는 웹 서버의 권한을 상속받으므로 SELinux port 정책때문에 오라클 DB 에 연결할 수 없다. 다음 명령어로 http 가 oracle의 tns listener(port 1521) 에 연결할 수 있도록 port 정책을 추가해야 한다. 

semanage port -a -p tcp -t http_port_t 1521

SELinux 설정을 변경하지 않으면 아래와 같이 ORA-12546 에러가 발생한다.

An Error occured! SQLSTATE[HY000]: pdo_oci_handle_factory: ORA-12546: TNS:permission denied (/home/lesstif/ora-client/PDO_OCI-1.0/oci_driver.c:463)


Exec Stack 

OCI 가 참고하는 오라클의 libclntsh  라이브러리내에서 stack 에서 실행되는 코드가 있어서 다음과 같은 에러가 발생한다.

PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib64/php/modules/pdo_oci.so' - libclntsh.so.12.1: cannot enable executable stack as shared object requires: Permission denied in Unknown on line 0

audit2why 명령어로 자세한 원인을 파악할 수 있다.

audit2why  < /var/log/audit/audit.log
 
type=AVC msg=audit(1422000769.943:40975): avc:  denied  { execstack } for  pid=27369 comm="httpd" scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:system_r:httpd_t:s0 tclass=process
        Was caused by:
        The boolean httpd_execmem was set incorrectly. 
        Description:
        Allow httpd scripts and modules execmem/execstack
        Allow access by executing:
        # setsebool -P httpd_execmem 1

execstack 유틸로 clntsh 라이브러리가 stack 에서 실행되도록 flag 를 설정한다. (execstack 는 ELF prelinking utility 패키지에 포함되어 있다.)

yum install prelink
oracle 12.1
execstack -c /usr/lib64/php/modules/oci8.so
execstack -c /usr/lib64/php/modules/pdo_oci.so
execstack -c /usr/lib/oracle/12.1/client/lib/libclntsh.so.12.1 



Ubuntu

vagrant 기준


  1. 컴파일러 설치

    apt-get install gcc make autoconf
  2. instant client 다운로드
  3. 복사

    sudo mkdir -p /usr/local/oracle
    sudo mv instantclient_11_2/  /usr/local/oracle
    sudo ln -s /usr/local/oracle/instantclient_11_2 /usr/local/oracle/instantclient
  4. oci 다운로드 및 컴파일

    pear download pecl/oci8
    tar zxvf oci8-2.0.8.tgz 
    cd oci8-2.0.8
    ./configure --with-oci8=shared,instantclient,/usr/lib/oracle/12.1/client64/lib
    sudo make install
  5. oci 로딩하도록 php 설정

    sudo echo 'extension=oci8.so' >> /etc/php5/mods-available/oci.ini 
    sudo ln -s /etc/php5/mods-available/oci.ini /etc/php5/fpm/conf.d/20-oci.ini
    sudo ln -s /etc/php5/mods-available/oci.ini /etc/php5/cli/conf.d/20-oci.ini

    PHP7

    sudo echo 'extension=oci8.so' >> /etc/php/7.0/mods-available/oci.ini
    ln -s /etc/php/7.0/mods-available/oci.ini /etc/php/7.0/fpm/conf.d/20-oci.ini
    ln -s /etc/php/7.0/mods-available/oci.ini /etc/php/7.0/cli/conf.d/20-oci.ini
  6. 재구동

    sudo service php5-fpm restart

    PHP7

    sudo service php7.0-fpm restart

PHP-FPM 연동

php-fpm 을 사용하는 경우 다음과 같이 Oracle 관련 환경 변수를 여러 파일에 설정해 주어야 정상적으로 동작한다.


  1. /etc/profile.d/oracle.sh 를 만들고 다음 내용 추가

    #!/bin/bash
    #This is the lib path were the instantclient is installed
    ORACLE_HOME=/usr/lib/oracle/11.2/client64
    #This locates C headers from your instantclient
    C_INCLUDE_PATH=/usr/include/oracle/11.2/client64
    #I've never had trouble with that but it might save you a couple debugging hours
    LD_LIBRARY_PATH=$ORACLE_HOME/lib
    NLS_LANG=KOREAN_KOREA.AL32UTF8
    export ORACLE_HOME LD_LIBRARY_PATH NLS_LANG
  2. php-fpm 의 설정 파일(/etc/php-fpm.d/www.conf)에 추가

    env[ORACLE_HOME] =/usr/lib/oracle/12.1/client64
    env[NLS_LANG] = KOREAN_KOREA.AL32UTF8
    env[LD_LIBRARY_PATH] = /usr/include/oracle/12.1/client64/lib:$LD_LIBRARY_PATH

     

  3.  /etc/init.d/php-fpm  에서 oracle 변수를 로딩하도록 변경

    # Additional environment file
    if [ -f /etc/sysconfig/php-fpm ]; then
          . /etc/sysconfig/php-fpm
    fi
     
    ## 오라클 설정 추가
    . /etc/profile.d/oracle.sh
  4. php-fpm 재구동

    service php-fpm restart

     

 

 

동작 확인

phpinfo

phpinfo 를 실행하는 php script 를 작성한 후에 결과에 oci 관련 내용이 있는지 확인한다.

OCI

다음 코드를 사용 환경에 맞게 수정한 후에 실행해서 정상 동작 여부를 확인한다.

oci-test.php
<?php
$srv = 'oracle.host';
$sid = 'SID';
$port = 1521;
$conn = oci_connect('username', 'password', "${srv}:${port}/${sid}", 'AL32UTF8');
if (!$conn) {
    $e = oci_error();
    echo "An Error occured! " .  $e['message'] . "\n";
    exit(1);
}   
$stid = oci_parse($conn, 'SELECT * FROM emp');
if (!oci_execute($stid)) {
    $e = oci_error($stid);
    echo "An Error occured! " .  $e['message'] . "\n";
    exit(1);
}
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
   var_dump($row) . "\n";
}
?> 


 같이 보기

Ref