关于PHP连接ORACLE问题
网络上有关PHP连接ORACLE的文章很多,但往往不够全面。一般只能解决部分问题,
本人结合实践,将全部过程写下来供各位参考
OS:bluepoint2.0
Java:
jre118_v3-glibc-2.1.3.tar
Oracle:
oracle8161.tar
这个网站可以下载多个版本的oracle
http://219.139.108.138/UnixSoft/Oracle/
至于JDK可以在这里下:
http://mirrors.ibiblio.org/pub/mirrors/blackdown/
bluepoint2.0就比较不好找了,我有ISO景象文件,不知那位可以提供FTP空间,不过其他的版本的linux问题也应该不大。
一、ORACLE的安装
网上关于ORACLE的安装的文章很多,如果在redhat7.1(类似内核的发行版)或以上的版本要安装ORACLE8i要解决glibc
兼容问题。bluepoint2.0与oracle816的Glibc库也不兼容但只需直接安装以下几个包就可以了:glib-1.2.6-3.i386.rpm
glib10-1.0.6-6.i386.rpm
glibc-2.1.3-15.i386.rpm
glibc-devel-2.1.3-15.i386.rpm
glibc-profile-2.1.3-15.i386.rpm
glib-devel-1.2.6-3.i386.rpm。
(这些包可以在redhat6.2的光盘中获得),不必安装glibc-2.1.3-stubs.tar.gz补丁,也无须改变gcc,ld等接连。具体的安装过程就不在这里讲了,只贴出我的环境,这个在下面的叙述中有用:
#------------------------------------------------------------------------
#
Setup
ORACLE
environment
#------------------------------------------------------------------------
ORACLE_HOME=/home/oracle/OraHome1
export
ORACLE_HOME
ORACLE_SID=orcl
export
ORACLE_SID
ORACLE_TERM=xterm
export
ORACLE_TERM
ORACLE_OWNER=oracle8
export
ORACLE_OWNER
TNS_ADMIN=/home/oracle/config/8.1.6
export
TNS_ADMIN
CLASSPATH=$ORACLE_HOME/jdbc/lib/classes111.zip:$CLASSPATH
export
CLASSPATH
LD_LIBRARY_PATH=$ORACLE_HOME/lib:$LD_LIBRARY_PATH
export
LD_LIBRARY_PATH
NLS_LANG="SIMPLIFIED
CHINESE"_CHINA.ZHS16GBK
export
NLS_LANG
#
Set
up
the
search
paths:
PATH=$PATH:/usr/local/jre/bin:bin:/sbin:/usr/sbin:$ORACLE_HOME/bin
PATH=$PATH:/usr/local/sbin:/usr/bin/X11:.
export
PATH
HOSTNAME
=.Cs163,这个在tnsnames.ora等文件配置中也有用。
在建库中名字选为ORACLE8,还有个问题,就是字符集选
ZHS16GBK,如果选择默认的US7ASCII,在windows客户端就会有中文显示问题,选了ZHS16GBK那就要在bash_profile设置NLS_LANG="SIMPLIFIED
CHINESE"_CHINA.ZHS16GBK
export
NLS_LANG
环境变量,这样在linux服务器端就可以正确中文显示问题了ZHS16GBK,当然前提是你得搞定linux中文问题。
如果建库时用了US7ASCII,在服务端中文没问题,但windows客户端就麻烦了,注册表改为和服务端一致即(Hkey_Local_machine\Software\Oracle\Home0\NLS_Lang
=
American.America.US7ASCII)甚至无法登陆,解决办法就是用ALTER
DATABASE改变数据库的字符集,当然这么做是有风险的,一般不提倡,不过我就是这么干过滴,以下是我的做法:
修改server端字符集(不建议使用)
在oracle
8之前,可以用直接修改数据字典表props$来改变数据库的字符集。但oracle8之后,至少有三张系统表记录了数据库字符集的信息,只改props$表并不完全,可能引起严重的后果。正确的修改方法如下:
$sqlplus
/nolog
SQL>conn
/
as
sysdba;
若此时数据库服务器已启动,则先执行SHUTDOWN
IMMEDIATE命令关闭数据库服务器,然后执行以下命令:
SQL>STARTUP
MOUNT;
SQL>ALTER
SYSTEM
ENABLE
RESTRICTED
SESSION;
SQL>ALTER
SYSTEM
SET
JOB_QUEUE_PROCESSES=0;
SQL>ALTER
SYSTEM
SET
AQ_TM_PROCESSES=0;
SQL>ALTER
DATABASE
OPEN;
SQL>ALTER
DATABASE
CHARACTER
SET
ZHS16GBK;
SQL>ALTER
DATABASE
national
CHARACTER
SET
ZHS16GBK;
SQL>SHUTDOWN
IMMEDIATE;
SQL>STARTUP
感觉有点离题,浪我们言归正传吧
二、apache和PHP的安装
以root用户登录,#mkdir
/www(这只是我的做法,你也可以mkdir
/dog
/pig之类)。#cd
/www
从网络下载apache和php的源代码,并把他们考到/www,
apache官方网站http://archive.apache.org/dist/httpd/
php官方网站:http://www.php.net/releases.php
我用的是apache_1.3.36,php-4.1.2
#tar
xvzf
apache_1.3.36.tar.gz
#tar
xvzf
php-4.1.2.tar
#cd
/www/apache_1.3.36
#./configure
--prefix=/www
#cd
/www/php-4.1.2
#configure
--with-apache=../apache_1.3.36
--with-oracle=/home/oracle/OraHome1
--with-oci8=/home/oracle/
OraHome1
--with-mysql
=/usr
--enable-url-includes
--enable-sockets
--enable-track-vars
--disable-debug
注意这里要根据各自的安装目录来配置
#make
#make
install
#cp
./php.ini-dist
/usr/local/lib/php.ini
这样就完成了对php的配置和安装,下面继续安装apache
#cd
/www/apache_1.3.33
#./configure
--prefix=/www
--enable-module=all
--activate-module=src/modules/php4/libphp4.a
有文章里写成--activate-module=./src/modules/php4/libphp4.a
以我自己的经历,这样做configure会失败。
#make
#make
install
这样就完成了apache的安装,此时需要编辑/www/conf/httpd.conf文件,加上php4的支持。
由于我们在
编译PHP时已经静态支持PHP4模块,因此无须加LoadModule
php4_module
lib/apache/libphp4.so
之类的模块装入语句,加了http反而不能正确启动。但必须加上
AddType
application/x-httpd-php
.php
.phtml
AddType
application/x-httpd-php-source
.phps
否则apache无法正确识别PHP后缀。
/www/conf/httpd.conf很重要,下面解决ORACLE连接也要设置该文件。
下面就可以先测试一下了:
先启动http服务:/www/bin/apachectl
start
在windows客户端打开IE,在地址栏输入http://you_ip_address/
可以看到Apache
Web
Server
已經安裝成功的一个欢迎页面,
在测试php:以root登陆,#vi
/www/htdocs/phpinfo.php
输入以下内容:
<?php
phpinfo();
?>
:wq退出。
在IE地址栏输入http://you_ip_address/
phpinfo.php,可以看到一系列的表格,是关于我们安装的PHP的信息,说明PHP没问题。
测试php和oracle的连接:
同样在/www/htdocs/目录下创建ora1.php,
内容如下:
<?php
$conn=OCILogon("scott","tiger","localdb");
if($conn)
{
echo
"Oracle
connect
successfully!";
}
else
{
echo
"Oracle
connect
failed!";
}
?>
在IE地址栏输入http://you_ip_address/
phpinfo.php
如果你能看到Oracle
connect
successfully,那恭喜你成功了,也就不必再看我下面的这些废话了。通常你不可能有这么好的运气,一般会出现:
Warning:
_oci_open_server:
ORA-12541:
TNS:no
listener
in
/www/htdocs/ora1.php
on
line
2
Oracle
connect
failed!
Warning:
_oci_open_server:
Connection
Failed:
Error
while
trying
to
retrieve
text
for
error
ORA-12546
Warning:
_oci_open_server:
TNS-12154
(ORA-12154):TNS:could
not
resolve
service
name
之类的出错信息。要解决这个问题就比较复杂了,要从unix/linux访问控制,TSN连接等方面入手去分析。有人认为这和环境变量有关,加了一大堆putenv之类的,其实没什么大用。
我们再来次试验将$conn=OCILogon("scott","tiger","localdb");
改为conn=OCILogon("scott","tiger");然后打开文件
/www/conf/httpd.conf
,将其中的User
nobody,Group
nobody,改为User
Oracle,Group
dba。再来试试看,应该就能成功,当然你首先是要启动http及数据库实例。
启动数据库有多种做法,这里用SVRMGR工具:
以oracle用户登陆linux
oracle$svrmgrl
SVRMGR>connect
internal
SVRMGR>startup
不过这样做好象不是很好(安全性是不是会有问题,我也不太知道),而且我们还不知道这样做会成功的道理。所以本文还是要继续写下去。
三、Oracle访问和TNS配置
要真正明白这个问题估计要写本书了,这里只提及对本问题最重要的一些内容:
支持网络连接的服务类型有dedicated方式,MTS方式。Dedicated是专用方式,MTS是共享方式,可用select
server
from
v$session;来确定你的服务类型,不过这个和我们没有大的关系,只在配置tnsnames.ora用到一点。
下面要澄清一个比较重要的事情:
用户连接到Oracle
服务有两种情形:
1. Oracle本地用户连接数据库
这种情况无须启用listener进程,也用不到listener.ora、sqlnet.ora,
tnsnames.ora等配置文件,连接的格式为:
sqlplus
scott/tiger。
非oracle用户要连接成功的话,还需要设置好oracle环境和确定对$ORACLE_HOME有访问权限。
2. 远程用户连接数据库服务器(TNS连接)
这种情况须启用listener进程,在用lsnrctl
start启用listener进程前还需要在服务器配置好listener.ora文件.
连接的格式为:
sqlplus
scott/tiger@localdb
当然要连接成功在客户机端还要做很多事情:在客户端机器上安装ORACLE的Oracle
Net通讯软件,正确配置sqlnet.ora,
tnsnames.ora文件。
如果本地用户一定要以TNS连接的方式,也不是不可以,但需要在服务器端同时配置listener.ora、sqlnet.ora、
tnsnames.ora。listener.ora给listener进程用,后两个给本地用户连接时查询服务名用,这里的localdb是我在tnsnames.ora中配的服务名。(不过有个问题很奇怪现在还不明白:在sqlnet.ora、
tnsnames.ora
不配置情况下,
svrmgrl中用connect
internal无须输入密码,但配了以后却要求输入orcle用户的linux帐户密码)
为了说明这三个文件的配置方法,先说明以下的概念:
主机识别:在/etc/hosts中
数据库识别:在/etc/oratab中
服务识别:$ORACLE_HOME/network/admin/tnsnames.ora
默认的情况其他两个配置也放在这里,当然你可以用环境变量TNS_ADMIN来设置新的路径,即便你设置了TNS_ADMIN,但$ORACLE_HOME/network/admin这个路径也还是能被识别。
下面边贴我的这三个配置文件边来分析其意义:
1.listener.ora
LISTENER
=
(DESCRIPTION
=
(ADDRESS
=
(PROTOCOL
=
TCP)(HOST
=
.cs163)(PORT
=
1521))
)
SID_LIST_LISTENER
=
(SID_LIST
=
(SID_DESC
=
(GLOBAL_DBNAME
=
Oracle8)
(ORACLE_HOME
=
/home/oracle/OraHome1)
(SID_NAME
=
ORCL)
)
)
(PROTOCOL
=
TCP)(HOST
=
.cs163)(PORT
=
1521),分别是协议,主机名在/etc/hosts中描述,oracle服务侦听的端口号。
GLOBAL_DBNAME
=
oracle8
为创建数据库时的数据库名
ORACLE_HOME就不讲了
SID_NAME为实例名
如果用户想要监听多个服务,只需在SID_LIST_LISTENER中的SID_LIST(监听列表)下再增加一个要监听的SID_DESC条目
,这样监听器就可以监听指定的ORACLE_SID服务了(即oracle的实例)
2.sqlnet.ora
SQLNET.AUTHENTICATION_SERVICES=
(NTS)
NAMES.DIRECTORY_PATH=
(TNSNAMES,HOSTNAME)
这个配置和tnsnames.ora配合使用,当用户输入sqlplus
system/manager@localdb后,sqlplus程序会自动到sqlnet.ora文件中找NAMES.DEFAULT_DOMAIN参数,假如该参数存在,则将该参数中的值取出,加到网络服务名的后面,
这里的配置没有NAMES.DEFAULT_DOMAIN参数,如果客户端输入sqlplus
sys/oracle@localdb,那么客户端就会首先在tnsnames.ora文件中找localdb的记录(服务名).如果没有相应的记录则尝试把localdb当作一个主机名,通过网络的途径去解析它的ip地址然后去连接这个ip上的GLOBAL_DBNAME=orcl这个实例;如果NAMES.DIRECTORY_PATH=
(TNSNAMES)那么客户端就只会从tnsnames.ora查找,找不到就报错。TNS-12154
(ORA-12154):TNS:could
not
resolve
service
name
3.tnsnames.ora
localdb=
(DESCRIPTION
=
(ADDRESS_LIST
=
(ADDRESS
=
(PROTOCOL
=
TCP)(HOST
=
192.168.1.29)(PORT
=
1521))
)
(
CONNECT_DATA
=(SERVICE_NAME
=
oracle8)
(SERVER
=
DEDICATED)
)
)
(PROTOCOL
=
TCP)(HOST
=
192.168.1.29)(PORT
=
1521))这些前面已经讲过了,(SERVER
=
DEDICATED)这个也讲过了,一般默认是DEDICATED,MTS方式需要专门的配置,可以用select
server
from
v$session核实,(SERVICE_NAME
=
oracle8)参数是oracle8i新引进的在8i以前,我们用SID来表示标识数据库的一个实例,但是在Oracle的并行环境中,一个数据库对应多个实例,这样就需要多个网络服务名,设置繁琐。为了方便并行环境中的设置,引进了Service_name参数,该参数对应一个数据库,而不是一个实例,而且该参数有许多其它的好处。,参数的缺省值为数据库名,可以用show
parameter
service_name
来确定其值。
TNS的配置就讲这些了,仅仅是其中的皮毛,但对分析我们的问题也就差不多了。更加多的内容可参考:《在Oracle网络结构中解决连接问题》一文:
http://fanqiang.chinaunix.net/db/oracle/2006-06-29/4714.shtml
四、Oracle和PHP连接错误的分析
先声明以下分析都是基于数据库实例已经启动的情况
第一次测试用$conn=OCILogon("scott","tiger","localdb");来连接Oracle。实际上就是
sqlplus
scott/tiger@localdb
这明显就是一个TNS连接请求,但listener.ora没被正确配置或
listener进程没启用,显然会产生错误,Warning:
_oci_open_server:
ORA-12154:
TNS:could
not
resolve
service
name
in
/www/htdocs/ora1.php
on
line
2
Oracle
connect
failed!
上面是典型的出错信息。但如果确定以上步骤都没问题的情况下,就一定没问题吗?我们用$conn=OCILogon("scott","tiger");来代替$conn=OCILogon("scott","tiger","localdb");进一不来测试
结果还是产生如下错误:
Warning:
_oci_open_server:
Error
while
trying
to
retrieve
text
for
error
ORA-12546
in
/www/htdocs/ora1.php
on
line
2
Oracle
connect
failed!
这时就要考虑另外一个重要问题了,那就是连接的用户要对$ORACLE_HOME有访问权限,在检查访问权限中我看到/home/oracle的权限是drwx-------,问题找到了,/www/conf/httpd.conf
中的User
nobody,Group
nobody显然没有权限。
解决办法:用root登陆到系统,#chmod
x
/home/oracle
#chmod
r
/home/oracle在来测试,不管是
$conn=OCILogon("scott","tiger","localdb")
还是$conn=OCILogon("scott","tiger")都成功了。
当然在listener没启动的情况,
$conn=OCILogon("scott","tiger","localdb")还是不行
我们在来看看,最初的第二次测试成功的情况:
www/conf/httpd.conf
中的User
nobody,Group
nobody改成了oracle.dba显然访问权限没问题$conn=OCILogon("scott","tiger")用本地连接的方式,故也不存在TNS问题。如果当初用$conn=OCILogon("scott","tiger","localdb")或
$conn=OCILogon("scott","tiger","XXXXXX")什么的,就算www/conf/httpd.conf
中已经改为了oracle.dba,在TNS没有被正确配置前也是不可能成功的。