RHEL/CentOS/Ubuntu 와 Windows 환경의 PHP 에서 오라클(Oracle) 사용하기
PHP 용 Oracle Driver 인 OCI8/PDO_OCI extension 을 설치하는 방법을 설명.
oracle instant client 12 부터는 9i 를 지원하지 않으므로 9i 에 연결하려면 11 버전을 설치해야 한다.
Windows
- 오라클 사이트에 연결하여 인스턴스 클라이언트를 다운받고 압축 해제(Ex: C:\devel\instantclient_12_1)
- Windows 의 "고급 시스템 설정"에 들어가서 환경변수 → Path 에 위에서 압축 해제한 경로를 추가
php.ini 에서 다음 항목 주석 해제
Oracle 12C Instant Clientextension=oci8_12c extension=pdo_oci
- 웹 서버 및 PHP 재구동
제대로 로딩되었는지 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 에서 오라클과 연동하려면 드라이버를 직접 빌드해야 한다.
사전 작업
gcc 컴파일러와 개발 도구 설치
yum groupinstall "Development Tools"
PHP 를 빌드하기 위한 개발 패키지 설치
WebTatic 저장소에서 PHP 5.5 를 설치했을 경우이며 CentOS 기본 PHP(php) 패키지나 PHP 5.4(php54w) 의 경우 패키지명을 다르게 적어 줘야 한다.
yum install php55w-pear php55w-devel zlib zlib-devel bc libaio glibc
오라클 사이트에 연결하여 Oracle run time library 와 개발툴인 Instance client 를 내려 받는다.
12.1.0.2.011.2.0.4.0
패키지를 설치한다.
yum localinstall oracle-instantclie*rpm
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
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 기준
컴파일러 설치
apt-get install gcc make autoconf
- instant client 다운로드
복사
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
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
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
재구동
sudo service php5-fpm restart
PHP7
sudo service php7.0-fpm restart
PHP-FPM 연동
php-fpm 을 사용하는 경우 다음과 같이 Oracle 관련 환경 변수를 여러 파일에 설정해 주어야 정상적으로 동작한다.
/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
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
/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
service php-fpm restart
동작 확인
phpinfo
phpinfo 를 실행하는 php script 를 작성한 후에 결과에 oci 관련 내용이 있는지 확인한다.
OCI
다음 코드를 사용 환경에 맞게 수정한 후에 실행해서 정상 동작 여부를 확인한다.
<?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"; } ?>