PECL(PHP Extension Community Library)是 PHP5 的一个重要特性。它避免了以往繁多的 PHP 扩展安装方式,采用了统一的安装方法。其中的面向对象的嵌入式 Perl 扩展编程与统一数据访问接口 PDO(PHP Data Objects)使得开发人员可以直接使用 Perl 的语言特性与优势并获得统一的数据库访问接口对 WEB 应用程序进行快速开发。在实例代码中,您将看到一个简单的基于 WEB 的服务器网络连通性测试工具,同时应用 PHP Perl 扩展并结合 PDO 操作 DB2 数据库进行基本介绍与演示
简介
拥有更成熟面向对象语法的 PHP 5 发布之后,基于 PHP 面向对象新特征的一些 PECL 扩展也得到了极大的发展。本文的主要目的就是通过一个简单的应用展示两个基于 PHP5 面向对象特性的 PECL 扩展的安装部署及编程方法。
PECL
PECL 的全称是 The PHP Extension Community Library ,是一个开放的并通过 PEAR(PHP Extension and Application Repository,PHP 扩展和应用仓库)打包格式来打包安装的 PHP 扩展库仓库。通过 PEAR 的 Package Manager 的安装管理方式,可以对 PECL 模块进行下载和安装。与以往的多数 PEAR 包不同的是,PECL 扩展包含的是可以编译进 PHP Core 的 C 语言代码,因此可以将 PECL 扩展库编译成为可动态加载的 .so 共享库,或者采用静态编译方式与 PHP 源代码编译为一体的方法进行扩展。PECL 扩展库包含了对于 XML 解析,数据库访问,邮件解析,嵌入式的 Perl 以及 Pthyon 脚本解释器等诸多的 PHP 扩展模块,因此从某种意义上来说,在运行效率上 PECL 要高于以往诸多的 PEAR 扩展库。
在使用 PECL 对 PHP 进行扩展安装过程中,需要用户熟悉 PEAR 包的安装方法以及 pear 命令。使用 PEAR Package Manager 可以直接下载,编译并安装 PECL 扩展库至 PHP 的扩展目录中,用户需要自行将扩展库的支持在 php.ini 文件中加以配置,或直接使用 dl() 函数在运行时进行动态加载以使用 PECL 的扩展库功能。
关于 PEAR Package Manager 详细的使用方法,请参阅 http://pear.php.net 上的相关文档。本文也将在下面的示例中给出使用 PEAR Package Manager 进行 PHP 的 PECL 扩展库安装的示例。
嵌入式 Perl 解释器扩展
PHP 的 Perl 扩展允许在 PHP 代码中直接使用 Perl 代码。这实际是一个内嵌的 Perl 的解释器,能够在 PHP 和 Perl 之间进行数据的交换。目前该扩展仅支持从 PHP 到 Perl 的单向接口,在将来会实现双向接口。目前 PHP 的 Perl 扩展允许程序员使用 PHP 脚本做以下事情:
- 加载并执行 Perl 文件
- 执行 Perl 代码
- 访问和修改 Perl 变量
- 调用 Perl 函数
- 初始化 Perl 对象
- 访问 Perl 对象的属性
- 调用 Perl 对象的方法
所有的这些特征都是通过一个简单的 API 类—— Perl 实现的。PHP 的 Perl 扩展的安装部署方法将在后面介绍,这里先来讨论该扩展的使用方法。从 PHP 中访问 Perl 的解释器,首先需要创建一个 Perl 类的实例。
清单 1. 初始化 Perl 对象
<?php $Perl = new Perl(); ?> |
上面的代码创建了一个 Perl 解释器的实例,该扩展允许在 PHP 代码环境中同时创建这个解释器的多个实例,但是他们在内部将使用同一个解释器,因此所有的变量和代码都将通过实例共享。对象 $perl 可以用来访问和修改 Perl 变量,调用 Perl 函数,执行 Perl 代码,加载并执行外部 Perl 文件等。其中外部文件可以通过 Perl:require() 方法加载。这个函数不返回任何值,如果要加载的 Perl 文件不存在或无效,这将会导致抛出 PHP 异常。
清单 2. Perl 实例操作示例
<?php
$perl = new Perl();
$perl->require(test.pl); //load external file – test.pl
var_dump($perl->x); // print scalar Perl variable - $x
var_dump($perl->array->x); // print array Perl variable - @x
var_dump($perl->hash->x); // print hash Perl variable - %x
$perl->func(); // call Perl function 'func' in void context
$x = $perl->func(); // call Perl function 'func' in scalar context
$y = $perl->array->func(); // call Perl function 'func' in array context
$y = $perl->hash->func(); // call Perl function 'func' in hash context
?>
执行 Perl 代码,Perl 代码可以通过 eval() 来执行,当要执行的 Perl 代码出现错误时抛出PHP异常。
清单 3. 执行 Perl 代码示例
<?php $perl = new Perl(); $perl->eval('use Digest::MD5');//load perl class MD5 echo $perl->{'Digest::MD5::md5_hex'}('perl2php'); var_dump($perl->md5_hex(“Hello”)); $perl->eval('require "TEST.pm";');//load a perl class file echo $perl->eval($x.'+'.$y.';'); // caculate and print the result of x+y $perl->eval('$z='.$x.'+'.$y.';'); echo $z; $perl->eval(‘ sub sum{ my $x = shif(@_); foreach my $y (@_){ $total + =$y; } return $total } ’); echo $perl->eval(“sum(1,2,3,4)”).”.”; ?> |
默认情况下 Perl 代码在标量上下文中被执行,但是也可以指定其在 array 或者 hash 上下文中执行
清单 4. 在 array 和 hash 上下文执行 Perl 代码
<?php $perl = new Perl(); $perl->eval('("a","b","c")'); // eval in void context var_dump($perl->eval('("a","b","c")')); // eval in scalar context var_dump($perl->array->eval('("a","b","c")')); // eval in array context var_dump($perl->hash->eval('("a","b","c")')); // eval in hash context ?> |
除此之外,也可以在 PHP 脚本中使用 Perl 对象,Perl 本身是一种面向对象的语言,但是对于类的定义没有具体的语法,在 Perl 中类可以看做是一个包,类的方法可以简单的认为是包中提供的一些函数,这些函数把该对象的引用作为第一个参数。Perl 扩展允许开发人员在 PHP 脚本中初始化 Perl 对象,并允许直接访问该对象的属性和方法。在PHP 中使用 Perl 对象,同样需要使用 Perl() 这个构造函数,但与前面的方法不同的是,需要传递参数给该构造函数。语法如下:
清单 5. 嵌入式的 Perl 解释器对象
new Perl($perl_class_name[, $constructor = "new"[perl_para,…]]) |
其中第一参数是 Perl 的类(包)名,第二个参数是构造函数的名字(可选)。如果有其他参数,这些参数将被传递给 Perl 的构造器。如果第二个参数被省略,默认的构造函数 new() 将被调用。
在 PHP 的脚本中初始化一个 Perl 对象之后便可以跟使用 PHP 自身的对象一样进行使用,唯一的不同点是方法调用的上下文不一样,在默认情况下,方法是在标量 scalar 上下文中被调用。要想在 array 或者 hash 上下文中进行调用,可以参照前面的方法:不直接通过 Perl 对象调用该方法,而应该通过一个具体的属性 array 或者 hash 进行调用。此外初始化 Perl 对象之后还可以在对象之间进行复制操作。
清单 6. 对象操作示例
<?php $x = new Perl("Test"); $y = new Perl("Test","Test",$x); $z = clone $y; echo $z->property; echo $z->method(1,2,3); // call method "f" in void context var_dump($x->method(1,2,3)); // call method in scalar context var_dump($x->array->method(1,2,3)); // call method in array context var_dump($x->hash->method(1,2,3)); // call method in hash context ?> |
在上面的实例代码中 Test 是一个 Perl 类,在 PHP 代码环境中可以用 require() 的方法将其包文件从外部引入也可以在 PHP 脚本中利用 Perl 对象的 eval 函数直接构造这样一个 Perl 类,实例的第一行将调用 Test 类中的 new 方法构造 Test 对象,而第二行将调用 Test 类中的 Test 方法构造 Test 对象。
统一的数据访问接口PDO
PDO(PHP Data Objects) 扩展为 PHP 访问数据库定义了一个轻量级的、一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,用户都可以通过统一的函数执行来查询和获取数据。注意,你并不能使用 PDO 扩展本身执行任何数据库操作,必须使用一个 database-specific PDO driver (针对特定数据库的 PDO 驱动)访问数据库服务器。
目前 PDO 支持如表1中数据库操作接口:
表 1. 支持 PDO 的驱动及相应的数据库列表
驱动程序名 | 所支持的数据库服务器 |
PDO_DBLIB | FreeTDS / Microsoft SQL Server / Sybase |
PDO_FIREBIRD | Firebird/Interbase 6 |
PDO_IBM | IBM DB2 |
PDO_INFORMIX | IBM Informix Dynamic Server |
PDO_MYSQL | MySQL 3.x/4.x/5.x |
PDO_OCI | Oracle Call Interface |
PDO_ODBC | ODBC v3 (IBM DB2, unixODBC and win32 ODBC) |
PDO_PGSQL | PostgreSQL |
PDO_SQLITE | SQLite 3 and SQLite 2 |
PDO 并不提供数据库抽象,它并不会重写 SQL 或提供数据库本身缺失的功能,如果用户需要这种功能,就必须使用一个更加成熟的抽象层。PDO 是随 PHP 5.1 发行,在PHP 5.0的 PECL 扩展中也可以使用。PDO 需要 PHP 5 核心面向对象特性的支持,所以它无法运行于之前的PHP 版本。
PDO 的安装部署方法将会在后面的章节介绍,此处先介绍 PDO 的使用。
连接和连接管理
数据库连接的建立可以通过创建一个 PDO 基类的实体实现,开发人员不用关心需要使用的是那个驱动,在代码中只需要使用 PDO 的类名。构造函数接受一个参数来表明数据源(也就是通常所知的 DSN)。另外还有三个参数是可选的,如果数据库服务器的连接需要提供用户名和密码,则需要在构造函数中指定;在该构造函数中,同时还可以通过第三个可选参数传递连接类型等的信息。
清单 7. 建立 PDO 连接(以 DB2 Server为例)
<?php function Connect() { try{ $dbh = new PDO(‘odbc:test’,‘user’,‘password’, array(PDO_ATTR_PERSISTENT =) true)); } catch(PDOException $e) { echo 'Connection failed: ' . $e->getMessage(); die(); } return $dbh; } ?> |
odbc:test 告诉 PDO 它所应该使用的 ODBC 驱动程序,并且应该使用 "test" 数据库。如果使用一个驱动程序管理器,那么可以用一个 ODBC 级数据源名称替代 "test"。在冒号字符之后可以指定任何有效的 ODBC 数据源连接字符串。接下来分别是用来连接数据库的用户名和密码。通过设定 PDO_ATTR_PERSISTENT 的值为 true 可以建立一个到数据库的持久连接,默认情况下不指定此参数建立的连接在脚本运行完毕或者所有对该 $dbh 的应用都被是释放的时候连接就会被断开。在某些情况应用程序的设计者只希望建立一次连接,以后对该数据库所有的访问操作都通过该连接实现。这时候就可以建立持久连接,持久连接建立后会被缓存并且在其他的数据库操作脚本访问该数据源的时候被重用。
断开连接
默认情况下,当建立的连接是非持久连接时候,当脚本结束时候连接将会断开,也可以通过显性的将 $dbh 变量设置为 null 来断开连接。
清单 8. 断开 PDO 连接
<?php function Disconnect($dbh) { $dbh = null; } ?> |
执行数据库操作
建立了与数据库的连接之后就可以对数据库进行 SQL 查询操作,对于无返回结果集的数据库操作(例如删除,更新,插入等) PDO 通过一个统一的函数 exec() 来实现,这个函数的返回值通常是数据库操作影响的行数,以删除为例,函数的返回结果是删除的行数。而对于需要返回数据结果集的查询操作则通过 query() 函数来实现。它将返回一个 PDOStatement 类型的结果集合。对于 PDOStatement 类型的结果集可以用它的各种操作方法(例如 fetch() 等)来操作结果集里面的数据。
清单 9. 执行数据库操作
<?php //issues a SQL statement and returns the number of affected rows. function Execute($execStr) { try{ $dbh = Connect();//引用清单1中的方法 $result = $dbh->exec($execStr); } catch (Exception $e) { } return $result; } //issues a SQL statement and returns a result set. function Query($queryStr) { try{ $dbh = Connect();//引用清单1中的方法 $result = $dbh->query($queryStr); } catch (Exception $e) { } return $result; } ?> |
事务和自动提交
PDO 的事务操作以自动提交模式运行,也就是说如果数据库支持事务,那么所运行的每一个查询都有它自己的隐式事务,如果数据库不支持事务,每个查询就没有这样的事务。如果需要启动一个事务操作,必须使用 PDO::beginTransaction() 方法来启动。这里需要注意的是:如果底层驱动程序不支持事务,那么将抛出一个 PDOException。在一个事务中,可以使用 PDO::commit() 或 PDO::rollBack() 来结束该事务,这取决于事务中运行的代码是否成功。
清单 10. 运用事务操作进行批处理
<?php //issues a set of SQL statement during a transaction function Execute (array $tranStr) { try{ $dbh = Connect();//引用清单1中的方法 $dbh->beginTransaction(); foreach($transStr as $stateStr) { $dbh->exec($stateStr); } $dbh->commit() } catch (Exception $e) { $dbh->rollBack(); } return $result; } ?> |
在上面的函数中所要执行的一系列 SQL 命令包括在 beginTransaction() 和 commit() 调用中,可以保证在更改完成之前,其他人无法看到更改。如果发生了错误,catch 块可以回滚事务开始以后发生的所有更改。这样可以有效的保证数据的一致性,在需要同步进行操作的数据更新中有重要应用。当脚本结束或当一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO 将自动回滚该事务。
预处理语句
首先需要解释一下预处理语句,通常将预处理语句看作要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。以运行一个重复插入数据为例,当往一个表中反复插入不同的数据的时候,使用预处理语句只需要编译一次,每次插入的时候只需要将变量用具体的参数取代即可。这可以在很大程度上提高数据库的性能。大部分成熟的数据库都支持预处理语句,即使驱动程序不支持预处理语句,PDO 也将仿真预处理语句。
清单 11. 使用预处理语句
<?php $dbh = Connect(); $prepareStatement = $dbh->prepare(“INSERT INTO STUDENT (id,name) VALUES (:id,:name)”); //bind the placeholder with a specify variable. $prepareStatemnet->bindParam(‘:id’, $id); $prepareStatement->bindParam(‘:name’, $name); //insert records. $id = ‘1’; $name = ‘user1; $prepareStatement->execute(); $id = ‘2’; $name = ‘user2; $prepareStatement->execute(); ?> |
示例程序:一个简单的网络连通性测试工具
在这个简单的示例程序中,使用 Perl 编写一个检查指定的服务器是否可以通过操作系统 ping 命令测试网络连通的函数,并通过 PHP 调用该方法获取测试结果,同时将测试的结果存入基于 DB2 的数据库中。在这个基于 WEB 的示例程序中,对于测试服务器网络是否可连接的 Perl 语言代码在附件的实例代码中,读者可以下载阅读。本文将不对该部分代码做解释,而将主要的精力放在描述 PHP 调用 Perl 代码的方法和 PDO 操作数据库的方法上。读者可以在本文的代码下载中获取到完整的该程序的源代码。
开发环境的搭建
本演示例程由数个样本程序组成,并使用了下列组件及开发测试工具:
操作系统:RedHat Enterprise Linux Server 5.0
- WEB 服务器:Apache 2.2.8
- 数据库服务器:DB2 Server Enterprise Edition 9
- PHP:5.0.0/5.2.6
- Perl:5.8.0
对于 PHP 和 Apache 的安装部署在本文中不做重复介绍,以下仅介绍 PDO 和 Perl 扩展的安装与部署方法。
PHP Perl扩展的安装与部署
PHP 的 Perl 扩展可以在 PECL 的网站http://pecl.php.net/package/perl获得下载。为了正确安装和使用该扩展,操作系统的软件环境必须满足以下需求:
- PHP 5.0.0 RC2 以上版本
- Perl 5.8.0 以上版本
编译扩展文件,PHP_PREFIX 和 PERL_PREFIX 必须指向 PHP 和 Perl 的安装路径。
#export PHP_PREFIX="/usr" #export PERL_PREFIX="/usr" #$PHP_PREFIX/bin/PHPize #./configure --with-Perl=$PERL_PREFIX --with-PHP-config=$PHP_PREFIX/bin/PHP-config #make |
安装(这一步需要根用户特权)
#make install |
在 php.ini 中添加 Perl 扩展 (这一步需要根用户特权)
extension=Perl.so |
PDO 扩展的安装部署
PHP 5.1 发布时附带了 PDO,但是也可以通过 PECL 这个 PHP 扩展库来结合使用 PDO 和 (1) PHP 5.0.3 及以上版本的安装。
在示例应用程序的开发中,所使用的是一个 DB2 数据库服务器。需要注意的一点是在安装PDO 时需要 ODBC 驱动,这就需要在本地安装有 DB2 管理客户机,否则编译将失败。
在 PHP 5.0.3 及以上版本上通过 PECL 进行安装
默认情况下,PHP 将安装 "PEAR" 包管理系统。为了成功地安装 PDO,需要升级到 PEAR 1.3.5(需要跟用户特权);使用以下命令
#pear upgrade PEAR |
接下来就可以安装 PDO 了(同样需要根用户特权):
#pear install PDO
安装完 PDO 核心后,为了使之生效,需要在 php.ini 文件中添加以下内容:
extension=pdo.so |
接下来需要安装用于 PDO 的 ODBC 驱动程序
#pear install PDO_ODBC |
按照提示输入 ODBC 驱动程序的类型和驱动程序的位置,在示例程序中使用的是 DB2 服务器,如果采用 DB2 的默认安装选项,可以直接输入 ibm-db2,如果选择了不同的安装位置,就要用实际的位置替换 /home/db2inst1/sqllib。由于 PEAR 不会为用户配置 PECL 扩展,因此在安装完成后需要将驱动程序添加到 php.ini 文件中,从而在PHP中正确加载驱动程序。确保将下面的内容添加在之前所添加的 pdo.so 这一行之后,否则 PHP 将不能正确地初始化。
extension=pdo_odbc.so
(2) PHP 5.1及以上版本的安装
PHP 5.1 发布时附带了 PDO,为获得 DB2 支持,只需在编译 PHP 时将下面的开关添加到配置行。
#tar xzvf php-5.2.6.tar.gz #cd php-5.2.6 #./configure –with-pdo-odbc=ibm-db2,/home/db2inst1/sqllib --with-apxs2=/usr/sbin/apxs #make #make install |
安装完毕后,应该完全重新启动 WEB 服务器,以确保 PHP 装载新的扩展,这样就可以开始使用 PDO 了。如果您使用的是 UNIX 平台,那么需要获得 DB2 客户机的 DB2 实例环境,以便正确地初始化,可以通过运行命令 source /home/db2inst1/sqllib/db2profile 来完成。
示例程序
该示例程序的流程和结构如下图所示,下面将分别介绍各个部分的功能及实现方法。读者可以在本文附带的示例代码中
图 1. 示例程序总体结构
测试服务器连通性的 Perl 模块(Connectivity Test Module)
代码文件:SERVERCHECK.pm
该模块采用 Perl 编写,使用 Perl 的系统调用函数 system() 执行 ping 命令,将标准输出输入到日志文件中,然后利用 Perl 的正则处理能力校验文件中的内容,从来判断服务器的是否能通过 ping 命令检测远端服务器的网络是否连通。在该函数中所调用的validate_cmd_response()函数会调用system()系统函数并捕获标准输出,同时验证标准输出中是否匹配指定字符串。
清单 12. 判断远端服务器网络连接性
sub server_is_connective { my( $class) = shift @_; my( $serverIp ) = @_; my( $pingOption,$validValue); $pingOption = '-c 2'; $validValue = ("2 packets transmitted, 2 received, 0% packet loss"); my($rtnValue) = &validate_cmd_response("ping $pingOption $serverIp",$validValue); print $rtnValue; } sub validate_cmd_response{ my( $cmd,$pValid) = @_; if( $cmd ){ my ( @stdoutValue ) = &x_execute_cmd( $cmd ); if ( grep( /$pValid/i, @stdoutValue ) ){ return 1; } } return 0; } |
PHP-Perl函数连接器(PHP-Perl Connector)
代码文件:Connector.php
该部分实现一个是从 Perl 接口向 PHP 接口转化的连接方法,在该部分中将 Perl 类中的方法转化成PHP函数。
清单 13. PHP 调用 Perl
<?php public function php2perl($func_name,array $para_array) { $func_str = $func_name."(\"\","; $conn_str = ""; foreach($para_array as $para) {//add all parameters $func_str.= $conn_str."\"".$para."\""; $conn_str = ","; } $func_str .= ");"; try { ob_start(); $perl = new Perl(); //push the complete directory of perl file into @INC variable $perl->eval('push(@INC,"/web/www.domain.com/docs/sample");'); $perl->eval('use SERVERCHECK'); $result = $perl->eval("SERVERCHECK::$func_str"); $out = ob_get_contents(); ob_end_clean(); } catch(PerlException $excep){ echo "Perl error".$excep->getMessage()."\n"; $out = "Perl error"; } return $out; } ?> |
在实例代码中只给出了一个转换,如果 Perl 代码中有其他的接口,也可以按照相同的方式将其转化为 PHP 接口。只要在方法中给出要调用的 Perl 方法的名字,并将要传递的参数放到一个数组中作为第二个参数,然后调用 php2perl() 方法就可以实现。在所定义的 PHP 函数 is_server_connective() 中,通过直接调用 php2perl() 函数,可以直接获得 Perl 函数 server_is_connective() 的返回结果。
清单 14. 使用php2perl的方法将Perl函数加以封装
public function is_server_connective($server) { $para = array($server); $result = $this->php2perl('server_is_connective',$para); return $result; } |
数据库操作模块(DB Operation Module)
代码文件:
PdoOpt.php(PDO 通用操作类)
GeneralDbOpt.php(数据库通用操作类)
ServerData.php(serversample 表操作类)
该模块实现应用程序跟 DB2 服务器的通信,通过这个部分可以将数据库中需要测试网络连接的服务器信息(主机名和地址)查询出来,在完成服务器连通性测试之后将测试结果更新回数据库中。在这里将 PDO 的基本操作单独抽象为一个类 PdoOpt,当需要更换数据库的时候可以在该文件中更改相应的数据库驱动等信息即可。基于此类可以建立出对数据库数据进行增删改查等基本的操作的数据库通用操作类 GeneralDbOpt,下面的代码演示了该类使用 PdoOpt 类执行数据查询的方法。
清单 15. 通过 PdoOpt 从指定表中查找指定id的数据结果
public function select($table,$select_id) { $sel_str = "select * from $table "; if($select_id !== ""&& $select_id !== null) { $sel_str .= " where id =".$select_id; } try{ if($result = $this->pdoOpt->Query($sel_str)) { $rtn_value = $result->fetch(PDO::FETCH_ASSOC); } } catch(Exception $e) { echo $e->getMessage(); } return $rtn_value; } |
在此类基础之上构建对于具体表的操作方法。应用中以操作 serversample 表为例实现了一个 ServerData 类。所有对该表的操作都在该类中实现。以获取数据库中所有的服务器信息为例演示如何使用 GeneralDbOpt 类
清单 16. 从数据库中获取服务器列表
public function get_server_list() { $fields = array('id','name'); $tables = array ($this->table); return $this->general_db_opt->select_all($tables,$fields,''); } |
数据库说明:数据库表中只有一个简单的演示用表 serversample,该表中有 id,name,ip,status 四个字段,分别代表服务器的标识,主机名,IP地址和状态信息。具体信息可以参看附件中的数据库表结构信息。
页面展现模块(Web View)
为了更好更清晰的展现该示例应用程序的逻辑结构,在该部分简单采用了 Smarty 的模板技术,避免将PHP 代码夹杂到 HTML 代码中。只使用该技术将数据库中查出的服务器信息呈现到页面上,在 Smarty 的网站 http://smarty.php.net/ 可以下载安装该引擎。
清单 17. Smarty 简单示例
<?php require('/usr/local/lib/php/Smarty/Smarty.class.php'); define('_SMARTY_ROOT','/web/www.domain.com/Smarty'); $smarty = new Smarty(); $smarty->template_dir = _SMARTY_ROOT."/templates"; $smarty->compile_dir = _SMARTY_ROOT."/templates_c"; $smarty->cache_dir = _SMARTY_ROOT."/cache"; $smarty->config_dir = _SMARTY_ROOT."/configs"; $smarty->right_delimiter = '}>'; $smarty->left_delimiter = '<{'; ?> |
应用程序逻辑
首先获取服务器的信息并显示到页面上,可以通过 get_server_list() 方法获取到服务器的信息,然后将其赋值给 server_info 变量,Smarty 在页面上控制如何展现这些服务器信息。
清单 18. 从数据库中获取所有服务器的信息并在 WEB 页面呈现
<?php require('main.php'); require('ServerData.php'); $serverData = new ServerData(); $server_info = $serverData->get_server_list(); $smarty->assign('server_info',$server_info); $smarty->display('serverping.tpl'); ?> |
当点击页面上的 Test 按钮时候会获取当前按钮所指定的服务器的 id,从而获得所需要测试连通性的服务器信息,通过调用 connector 的方法测试,测试完毕后调用 ServerData 的更新方法将测试结果更新进数据库中指定 id 的服务器信息中。
清单 19. 从 PHP 端测试服务器连通性并将数据更新至数据库中
<?php require_once('Connector.php'); require_once('ServerData.php'); require('main.php'); $server_id =PECL(PHP Extension Community Library)是 PHP5 的一个重要特性。它避免了以往繁多的 PHP 扩展安装方式,采用了统一的安装方法。其中的面向对象的嵌入式 Perl 扩展编程与统一数据访问接口 PDO(PHP Data Objects)使得开发人员可以直接使用 Perl 的语言特性与优势并获得统一的数据库访问接口对 WEB 应用程序进行快速开发。在实例代码中,您将看到一个简单的基于 WEB 的服务器网络连通性测试工具,同时应用 PHP Perl 扩展并结合 PDO 操作 DB2 数据库进行基本介绍与演示 PECL PECL 的全称是 The PHP Extension Community Library ,是一个开放的并通过 PEAR(PHP Extension and Application Repository,PHP 扩展和应用仓库)打包格式来打包安装的 PHP 扩展库仓库。通过 PEAR 的 Package Manager 的安装管理方式,可以对 PECL 模块进行下载和安装。与以往的多数 PEAR 包不同的是,PECL 扩展包含的是可以编译进 PHP Core 的 C 语言代码,因此可以将 PECL 扩展库编译成为可动态加载的 .so 共享库,或者采用静态编译方式与 PHP 源代码编译为一体的方法进行扩展。PECL 扩展库包含了对于 XML 解析,数据库访问,邮件解析,嵌入式的 Perl 以及 Pthyon 脚本解释器等诸多的 PHP 扩展模块,因此从某种意义上来说,在运行效率上 PECL 要高于以往诸多的 PEAR 扩展库。 在使用 PECL 对 PHP 进行扩展安装过程中,需要用户熟悉 PEAR 包的安装方法以及 pear 命令。使用 PEAR Package Manager 可以直接下载,编译并安装 PECL 扩展库至 PHP 的扩展目录中,用户需要自行将扩展库的支持在 php.ini 文件中加以配置,或直接使用 dl() 函数在运行时进行动态加载以使用 PECL 的扩展库功能。 关于 PEAR Package Manager 详细的使用方法,请参阅 http://pear.php.net 上的相关文档。本文也将在下面的示例中给出使用 PEAR Package Manager 进行 PHP 的 PECL 扩展库安装的示例。 嵌入式 Perl 解释器扩展 PHP 的 Perl 扩展允许在 PHP 代码中直接使用 Perl 代码。这实际是一个内嵌的 Perl 的解释器,能够在 PHP 和 Perl 之间进行数据的交换。目前该扩展仅支持从 PHP 到 Perl 的单向接口,在将来会实现双向接口。目前 PHP 的 Perl 扩展允许程序员使用 PHP 脚本做以下事情:
所有的这些特征都是通过一个简单的 API 类—— Perl 实现的。PHP 的 Perl 扩展的安装部署方法将在后面介绍,这里先来讨论该扩展的使用方法。从 PHP 中访问 Perl 的解释器,首先需要创建一个 Perl 类的实例。 清单 1. 初始化 Perl 对象
上面的代码创建了一个 Perl 解释器的实例,该扩展允许在 PHP 代码环境中同时创建这个解释器的多个实例,但是他们在内部将使用同一个解释器,因此所有的变量和代码都将通过实例共享。对象 $perl 可以用来访问和修改 Perl 变量,调用 Perl 函数,执行 Perl 代码,加载并执行外部 Perl 文件等。其中外部文件可以通过 Perl:require() 方法加载。这个函数不返回任何值,如果要加载的 Perl 文件不存在或无效,这将会导致抛出 PHP 异常。 清单 2. Perl 实例操作示例 <?php $perl = new Perl(); $perl->require(test.pl); //load external file – test.pl var_dump($perl->x); // print scalar Perl variable - $x var_dump($perl->array->x); // print array Perl variable - @x var_dump($perl->hash->x); // print hash Perl variable - %x $perl->func(); // call Perl function 'func' in void context $x = $perl->func(); // call Perl function 'func' in scalar context $y = $perl->array->func(); // call Perl function 'func' in array context $y = $perl->hash->func(); // call Perl function 'func' in hash context ?> 执行 Perl 代码,Perl 代码可以通过 eval() 来执行,当要执行的 Perl 代码出现错误时抛出PHP异常。 清单 3. 执行 Perl 代码示例
默认情况下 Perl 代码在标量上下文中被执行,但是也可以指定其在 array 或者 hash 上下文中执行 清单 4. 在 array 和 hash 上下文执行 Perl 代码
除此之外,也可以在 PHP 脚本中使用 Perl 对象,Perl 本身是一种面向对象的语言,但是对于类的定义没有具体的语法,在 Perl 中类可以看做是一个包,类的方法可以简单的认为是包中提供的一些函数,这些函数把该对象的引用作为第一个参数。Perl 扩展允许开发人员在 PHP 脚本中初始化 Perl 对象,并允许直接访问该对象的属性和方法。在PHP 中使用 Perl 对象,同样需要使用 Perl() 这个构造函数,但与前面的方法不同的是,需要传递参数给该构造函数。语法如下: 清单 5. 嵌入式的 Perl 解释器对象
其中第一参数是 Perl 的类(包)名,第二个参数是构造函数的名字(可选)。如果有其他参数,这些参数将被传递给 Perl 的构造器。如果第二个参数被省略,默认的构造函数 new() 将被调用。 在 PHP 的脚本中初始化一个 Perl 对象之后便可以跟使用 PHP 自身的对象一样进行使用,唯一的不同点是方法调用的上下文不一样,在默认情况下,方法是在标量 scalar 上下文中被调用。要想在 array 或者 hash 上下文中进行调用,可以参照前面的方法:不直接通过 Perl 对象调用该方法,而应该通过一个具体的属性 array 或者 hash 进行调用。此外初始化 Perl 对象之后还可以在对象之间进行复制操作。 清单 6. 对象操作示例
在上面的实例代码中 Test 是一个 Perl 类,在 PHP 代码环境中可以用 require() 的方法将其包文件从外部引入也可以在 PHP 脚本中利用 Perl 对象的 eval 函数直接构造这样一个 Perl 类,实例的第一行将调用 Test 类中的 new 方法构造 Test 对象,而第二行将调用 Test 类中的 Test 方法构造 Test 对象。 统一的数据访问接口PDO PDO(PHP Data Objects) 扩展为 PHP 访问数据库定义了一个轻量级的、一致性的接口,它提供了一个数据访问抽象层,这样,无论使用什么数据库,用户都可以通过统一的函数执行来查询和获取数据。注意,你并不能使用 PDO 扩展本身执行任何数据库操作,必须使用一个 database-specific PDO driver (针对特定数据库的 PDO 驱动)访问数据库服务器。 目前 PDO 支持如表1中数据库操作接口: 表 1. 支持 PDO 的驱动及相应的数据库列表
PDO 并不提供数据库抽象,它并不会重写 SQL 或提供数据库本身缺失的功能,如果用户需要这种功能,就必须使用一个更加成熟的抽象层。PDO 是随 PHP 5.1 发行,在PHP 5.0的 PECL 扩展中也可以使用。PDO 需要 PHP 5 核心面向对象特性的支持,所以它无法运行于之前的PHP 版本。 PDO 的安装部署方法将会在后面的章节介绍,此处先介绍 PDO 的使用。 连接和连接管理 数据库连接的建立可以通过创建一个 PDO 基类的实体实现,开发人员不用关心需要使用的是那个驱动,在代码中只需要使用 PDO 的类名。构造函数接受一个参数来表明数据源(也就是通常所知的 DSN)。另外还有三个参数是可选的,如果数据库服务器的连接需要提供用户名和密码,则需要在构造函数中指定;在该构造函数中,同时还可以通过第三个可选参数传递连接类型等的信息。 清单 7. 建立 PDO 连接(以 DB2 Server为例)
odbc:test 告诉 PDO 它所应该使用的 ODBC 驱动程序,并且应该使用 "test" 数据库。如果使用一个驱动程序管理器,那么可以用一个 ODBC 级数据源名称替代 "test"。在冒号字符之后可以指定任何有效的 ODBC 数据源连接字符串。接下来分别是用来连接数据库的用户名和密码。通过设定 PDO_ATTR_PERSISTENT 的值为 true 可以建立一个到数据库的持久连接,默认情况下不指定此参数建立的连接在脚本运行完毕或者所有对该 $dbh 的应用都被是释放的时候连接就会被断开。在某些情况应用程序的设计者只希望建立一次连接,以后对该数据库所有的访问操作都通过该连接实现。这时候就可以建立持久连接,持久连接建立后会被缓存并且在其他的数据库操作脚本访问该数据源的时候被重用。 断开连接 默认情况下,当建立的连接是非持久连接时候,当脚本结束时候连接将会断开,也可以通过显性的将 $dbh 变量设置为 null 来断开连接。 清单 8. 断开 PDO 连接
执行数据库操作 建立了与数据库的连接之后就可以对数据库进行 SQL 查询操作,对于无返回结果集的数据库操作(例如删除,更新,插入等) PDO 通过一个统一的函数 exec() 来实现,这个函数的返回值通常是数据库操作影响的行数,以删除为例,函数的返回结果是删除的行数。而对于需要返回数据结果集的查询操作则通过 query() 函数来实现。它将返回一个 PDOStatement 类型的结果集合。对于 PDOStatement 类型的结果集可以用它的各种操作方法(例如 fetch() 等)来操作结果集里面的数据。 清单 9. 执行数据库操作
事务和自动提交 PDO 的事务操作以自动提交模式运行,也就是说如果数据库支持事务,那么所运行的每一个查询都有它自己的隐式事务,如果数据库不支持事务,每个查询就没有这样的事务。如果需要启动一个事务操作,必须使用 PDO::beginTransaction() 方法来启动。这里需要注意的是:如果底层驱动程序不支持事务,那么将抛出一个 PDOException。在一个事务中,可以使用 PDO::commit() 或 PDO::rollBack() 来结束该事务,这取决于事务中运行的代码是否成功。 清单 10. 运用事务操作进行批处理
在上面的函数中所要执行的一系列 SQL 命令包括在 beginTransaction() 和 commit() 调用中,可以保证在更改完成之前,其他人无法看到更改。如果发生了错误,catch 块可以回滚事务开始以后发生的所有更改。这样可以有效的保证数据的一致性,在需要同步进行操作的数据更新中有重要应用。当脚本结束或当一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO 将自动回滚该事务。 预处理语句 首先需要解释一下预处理语句,通常将预处理语句看作要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。以运行一个重复插入数据为例,当往一个表中反复插入不同的数据的时候,使用预处理语句只需要编译一次,每次插入的时候只需要将变量用具体的参数取代即可。这可以在很大程度上提高数据库的性能。大部分成熟的数据库都支持预处理语句,即使驱动程序不支持预处理语句,PDO 也将仿真预处理语句。 清单 11. 使用预处理语句
示例程序:一个简单的网络连通性测试工具 在这个简单的示例程序中,使用 Perl 编写一个检查指定的服务器是否可以通过操作系统 ping 命令测试网络连通的函数,并通过 PHP 调用该方法获取测试结果,同时将测试的结果存入基于 DB2 的数据库中。在这个基于 WEB 的示例程序中,对于测试服务器网络是否可连接的 Perl 语言代码在附件的实例代码中,读者可以下载阅读。本文将不对该部分代码做解释,而将主要的精力放在描述 PHP 调用 Perl 代码的方法和 PDO 操作数据库的方法上。读者可以在本文的代码下载中获取到完整的该程序的源代码。 开发环境的搭建 本演示例程由数个样本程序组成,并使用了下列组件及开发测试工具: 操作系统:RedHat Enterprise Linux Server 5.0
对于 PHP 和 Apache 的安装部署在本文中不做重复介绍,以下仅介绍 PDO 和 Perl 扩展的安装与部署方法。 PHP Perl扩展的安装与部署 PHP 的 Perl 扩展可以在 PECL 的网站http://pecl.php.net/package/perl获得下载。为了正确安装和使用该扩展,操作系统的软件环境必须满足以下需求:
编译扩展文件,PHP_PREFIX 和 PERL_PREFIX 必须指向 PHP 和 Perl 的安装路径。
安装(这一步需要根用户特权)
在 php.ini 中添加 Perl 扩展 (这一步需要根用户特权)
PDO 扩展的安装部署 PHP 5.1 发布时附带了 PDO,但是也可以通过 PECL 这个 PHP 扩展库来结合使用 PDO 和 (1) PHP 5.0.3 及以上版本的安装。 在示例应用程序的开发中,所使用的是一个 DB2 数据库服务器。需要注意的一点是在安装PDO 时需要 ODBC 驱动,这就需要在本地安装有 DB2 管理客户机,否则编译将失败。 在 PHP 5.0.3 及以上版本上通过 PECL 进行安装 默认情况下,PHP 将安装 "PEAR" 包管理系统。为了成功地安装 PDO,需要升级到 PEAR 1.3.5(需要跟用户特权);使用以下命令
接下来就可以安装 PDO 了(同样需要根用户特权): #pear install PDO 安装完 PDO 核心后,为了使之生效,需要在 php.ini 文件中添加以下内容:
接下来需要安装用于 PDO 的 ODBC 驱动程序
按照提示输入 ODBC 驱动程序的类型和驱动程序的位置,在示例程序中使用的是 DB2 服务器,如果采用 DB2 的默认安装选项,可以直接输入 ibm-db2,如果选择了不同的安装位置,就要用实际的位置替换 /home/db2inst1/sqllib。由于 PEAR 不会为用户配置 PECL 扩展,因此在安装完成后需要将驱动程序添加到 php.ini 文件中,从而在PHP中正确加载驱动程序。确保将下面的内容添加在之前所添加的 pdo.so 这一行之后,否则 PHP 将不能正确地初始化。 extension=pdo_odbc.so (2) PHP 5.1及以上版本的安装 PHP 5.1 发布时附带了 PDO,为获得 DB2 支持,只需在编译 PHP 时将下面的开关添加到配置行。
安装完毕后,应该完全重新启动 WEB 服务器,以确保 PHP 装载新的扩展,这样就可以开始使用 PDO 了。如果您使用的是 UNIX 平台,那么需要获得 DB2 客户机的 DB2 实例环境,以便正确地初始化,可以通过运行命令 source /home/db2inst1/sqllib/db2profile 来完成。 示例程序 该示例程序的流程和结构如下图所示,下面将分别介绍各个部分的功能及实现方法。读者可以在本文附带的示例代码中 图 1. 示例程序总体结构 测试服务器连通性的 Perl 模块(Connectivity Test Module) 代码文件:SERVERCHECK.pm 该模块采用 Perl 编写,使用 Perl 的系统调用函数 system() 执行 ping 命令,将标准输出输入到日志文件中,然后利用 Perl 的正则处理能力校验文件中的内容,从来判断服务器的是否能通过 ping 命令检测远端服务器的网络是否连通。在该函数中所调用的validate_cmd_response()函数会调用system()系统函数并捕获标准输出,同时验证标准输出中是否匹配指定字符串。 清单 12. 判断远端服务器网络连接性
PHP-Perl函数连接器(PHP-Perl Connector) 代码文件:Connector.php 该部分实现一个是从 Perl 接口向 PHP 接口转化的连接方法,在该部分中将 Perl 类中的方法转化成PHP函数。 清单 13. PHP 调用 Perl
在实例代码中只给出了一个转换,如果 Perl 代码中有其他的接口,也可以按照相同的方式将其转化为 PHP 接口。只要在方法中给出要调用的 Perl 方法的名字,并将要传递的参数放到一个数组中作为第二个参数,然后调用 php2perl() 方法就可以实现。在所定义的 PHP 函数 is_server_connective() 中,通过直接调用 php2perl() 函数,可以直接获得 Perl 函数 server_is_connective() 的返回结果。 清单 14. 使用php2perl的方法将Perl函数加以封装
数据库操作模块(DB Operation Module) 代码文件: PdoOpt.php(PDO 通用操作类) GeneralDbOpt.php(数据库通用操作类) ServerData.php(serversample 表操作类) 该模块实现应用程序跟 DB2 服务器的通信,通过这个部分可以将数据库中需要测试网络连接的服务器信息(主机名和地址)查询出来,在完成服务器连通性测试之后将测试结果更新回数据库中。在这里将 PDO 的基本操作单独抽象为一个类 PdoOpt,当需要更换数据库的时候可以在该文件中更改相应的数据库驱动等信息即可。基于此类可以建立出对数据库数据进行增删改查等基本的操作的数据库通用操作类 GeneralDbOpt,下面的代码演示了该类使用 PdoOpt 类执行数据查询的方法。 清单 15. 通过 PdoOpt 从指定表中查找指定id的数据结果
在此类基础之上构建对于具体表的操作方法。应用中以操作 serversample 表为例实现了一个 ServerData 类。所有对该表的操作都在该类中实现。以获取数据库中所有的服务器信息为例演示如何使用 GeneralDbOpt 类 清单 16. 从数据库中获取服务器列表
数据库说明:数据库表中只有一个简单的演示用表 serversample,该表中有 id,name,ip,status 四个字段,分别代表服务器的标识,主机名,IP地址和状态信息。具体信息可以参看附件中的数据库表结构信息。 页面展现模块(Web View) 为了更好更清晰的展现该示例应用程序的逻辑结构,在该部分简单采用了 Smarty 的模板技术,避免将PHP 代码夹杂到 HTML 代码中。只使用该技术将数据库中查出的服务器信息呈现到页面上,在 Smarty 的网站 http://smarty.php.net/ 可以下载安装该引擎。 清单 17. Smarty 简单示例
应用程序逻辑 首先获取服务器的信息并显示到页面上,可以通过 get_server_list() 方法获取到服务器的信息,然后将其赋值给 server_info 变量,Smarty 在页面上控制如何展现这些服务器信息。 清单 18. 从数据库中获取所有服务器的信息并在 WEB 页面呈现
当点击页面上的 Test 按钮时候会获取当前按钮所指定的服务器的 id,从而获得所需要测试连通性的服务器信息,通过调用 connector 的方法测试,测试完毕后调用 ServerData 的更新方法将测试结果更新进数据库中指定 id 的服务器信息中。 清单 19. 从 PHP 端测试服务器连通性并将数据更新至数据库中
当点击了每行右侧的 Test 按钮后,可以获得所测试的服务器 IP 地址的网络连接是否成功: 图 2. 示例程序 结束语 至此,本文已经介绍了基于 PHP 5 面向对象特性的两个 PECL 扩展的安装部署及使用方法,同时通过示例演示程序,基本介绍了这两个 PECL 扩展的具体应用方法。基于 PHP 5 面向对象新特性的诸多 PECL 扩展给 PHP 开发带来极大的便捷,也大大增强了 PHP 的语言扩展功能。如果您想在其他的环境下部署您的应用程序或者获得这两个基于 PECL 的扩展更为详细的信息,可以通过访问 PHP 和 PECL 的网站来获得这两个扩展的详细手册与说明。(责任编辑:A6) GET["serverid"]; $serverData = new ServerData(); $serverData->get_server_info($server_id); //get server ip information $server_ip = $serverData->ip; $connector = new Connector(); $result = $connector->is_server_connective($server_ip); //test server connectivity if($result) { //print test result and update server information } else { //print test result and update server information } $serverData->update_server_info($server_id);//update test result into database echo $rtn_str; ?> |
当点击了每行右侧的 Test 按钮后,可以获得所测试的服务器 IP 地址的网络连接是否成功:
图 2. 示例程序
结束语
至此,本文已经介绍了基于 PHP 5 面向对象特性的两个 PECL 扩展的安装部署及使用方法,同时通过示例演示程序,基本介绍了这两个 PECL 扩展的具体应用方法。基于 PHP 5 面向对象新特性的诸多 PECL 扩展给 PHP 开发带来极大的便捷,也大大增强了 PHP 的语言扩展功能。如果您想在其他的环境下部署您的应用程序或者获得这两个基于 PECL 的扩展更为详细的信息,可以通过访问 PHP 和 PECL 的网站来获得这两个扩展的详细手册与说明。(责任编辑:A6)