Chinaunix首页 | 论坛 | 博客
  • 博客访问: 91762846
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-04-30 22:58:32

使用带有 Apache 2 的 PHP 的统一 ODBC 扩展

级别: 中级

[@more@]

Dan Scott
产品经理,IBM DB2 Universal Database for Linux, IBM Canada
2005 年 3 月


PHP:超文本处理器(PHP:Hypertext Processor),是一种创建 Web 内容的、功能强大且越来越受欢迎的服务器端脚本语言。可移植性是 PHP 受到欢迎的主要原因之一:

  • 可以在 Microsoft Windows、Linux、BSD、Macintosh OS X 和 UNIX 服务器上编译和运行 PHP。
  • PHP 可以与包括 Apache 和 Microsoft IIS 在内的大多数普及 Web 服务器紧密集成,也可以用作独立的 CGI 解释器。
  • PHP 的源代码可以免费获取,您可以自由编写和分发 PHP 应用程序,将其用于商业和非商业用途。

开放源码社区积极支持围绕 PHP 语言进行的开发,并在这方面做出了热心贡献。作为他们努力的结果,PHP 语言提供了一个令人印象深刻的扩展集合来提供各种功能,从 XML 转换、动态生成图像和 Adobe 可移植文档格式(Portable Document Format,PDF)文件,到支持 SOAP 客户机和服务器。

在简要介绍 PHP 脚本编制之后,本文将描述如何将 PHP 编译成支持 DB2 客户机的动态装入的 Apache 模块。文章的最后是几个示例,这几个示例将演示如何使用 PHP 连接 IBM Cloudscape 或 IBM DB2 Universal Database 服务器,如何向这些服务器中插入数据,以及如何从中选择数据。本文更新了我的前一篇文章,将介绍堆栈各个方面的主要版本改进:从 PHP 4 到 PHP 5、从 Apache 1.3.x 到 2.0.x、从 DB2 UDB Version 7 到 Version 8,以及从基于 2.4 内核到基于 2.6 内核的 Linux 发行版本。


PHP 基本上是一种服务器端脚本编制语言,它允许您将应用程序逻辑嵌入 HTML 页面,或者用 PHP 函数创建整个 HTML 页面。当 Web 服务器收到对某个 PHP 页面的请求时,它就将控制权转交给 PHP 引擎。PHP 引擎装入 PHP 页面,执行该页面内所有的 PHP 函数,然后将生成的 HTML 返回给 Web 服务器。

要在 Web 页面内调用 PHP 函数,只需将 PHP 函数嵌入到常规的 HTML 源代码内即可。PHP 函数被嵌入到以 ?php 开始并以 ? 结尾的 HTML 样式的标签内。例如, 演示了对 PHP 函数 echo() 的调用。要在这个 PHP 标签内包含多个函数,可以使用分号(;)字符将这些函数分隔开。

当然,可以在一个 PHP 脚本内包含许多 PHP 标签。为了演示使用 PHP 编写 Web 页面是多么的容易,下面给出了一段简短的脚本,它将输出从任意值开始到任意值结束的一系列数字。


在前一篇文章,我向读者介绍了一些有问题的 PHP 编码实践。因为我认为在那个时候它们是处于主导地位的实践,但在最近的 4 年中,人们的 PHP 开发和安全性意识已经获得了显著的提高。第一个约定是:虽然 PHP 解释器允许您将 PHP 代码转义序列直接放在 HTML 内部,但是现在许多开发人员都在一个 块中编写一个 PHP 文件,并将 HTML 生成为代码流的一部分。



                if ($start > $stop) {
return(FALSE);
}
elseif ($increment <= 0) {
return(FALSE);
}
else {
for ($i = $start; $i < $stop; $i = $i + $increment) {
print "$i ";
}
}
return(TRUE);
}
?>

Counting from x to y

Counting from x to y


Counting from 1 to 10 by 1:


Counting from 2 to 20 by 2:



相比之下,目前许多 PHP 应用程序都将其代码分隔成一些函数,并从这些函数中生成 HTML。该方法使得理清用来生成最终 PHP 程序输出的调用顺序变得容易得多。在下面的例子中,我将前面的原始 HTML 移入它自己命名的 print_html() 函数中,这个函数是 PHP 程序中惟一调用的执行语句。



               
function print_sequence ($start, $stop, $increment) {
if ($start > $stop) {
return(FALSE);
}
elseif ($increment <= 0) {
return(FALSE);
}
$numbers = "

Counting from $start to $stop by $increment: ";
for ($i = $start; $i < $stop; $i = $i + $increment) {
$numbers .= "$i ";
}
$numbers .= "

n";
return($numbers);
}

function print_html() {
$html = '
Counting from x to y

Counting from x to y

'
. print_sequence(1, 10, 1)
. print_sequence(2, 20, 2)
. '

';

return($html);
}

echo print_html();

?>

该方法与将 PHP 函数收集到模块中的方法相结合的另一好处就是,您可以容易地将 PHP 页面的标题和脚注标准化为一个单独模块。对于整个 PHP 站点外观的修改可以通过在一个模块中修改标准标题和脚注函数来完成,无需更改站点上的每一个 PHP 页面。没有绝对正确或错误的方法:将 PHP 输出封装在函数中和将 PHP 代码插入 HTML 模板中是两种处理方法,找到两者之间的适当平衡是一种个人选择。您只需认识每种方法的优点,并选择最能满足您需求的方法即可。


本文假定您在 Network Server 模式下运行 DB2 Universal Database 服务器或 IBM Cloudscape 数据库。为了连接 DB2 或 Cloudscape 数据库服务器,PHP 的统一 ODBC 扩展需要依赖 DB2 客户机的调用级接口(Call Level Interface,CLI)层。必须在同一台计算机上安装 DB2 客户机作为 PHP 解释器。首先,要从 下载 DB2 Runtime Client。确保您为操作系统和‘位’级(32 位还是 64 位)选择了合适的客户机。


  1. 安装 DB2 客户机,确保选择了用于应用程序开发的头文件和库文件。
  2. 在创建 DB2 实例时,选择适合您需求的用户名和密码。出于本文的目的,我们将假定默认的用户是 db2inst1,密码为 ibmdb2
  3. 通过确保 /home/db2inst1/sqllib/include/ 中包含诸如 sqlcli1.h 的文件,来确认您已经正确安装了该应用程序开发组件。


为 Windows 操作系统提供了已预编译的二进制文件。这些二进制文件包括一个已经在 ODBC Driver Manager 上编译了的统一 ODBC 版本。这让我们可以权衡在安装无需进行编译就支持 DB2 和 Cloudscape 的 PHP 时需要考虑的一些性能因素。安装用于 Windows 的 DB2 客户机,并继续下一步,即建立到数据库的连接


  1. (只适用于 Linux):通过下列命令在 shell 会话中继承 DB2 实例环境:
    bash$ source /home/db2inst1/sqllib/db2profile 
  2. 编目要连接的节点和数据库。例如,要在运行在端口 1527 上的名为‘db2air.toronto.ibm.com’的服务器上对数据库‘SAMPLE’进行编目,需要发出下列命令:
    bash$ db2 CATALOG TCPIP NODE airnode REMOTE db2air.toronto.ibm.com PORT 1527
    bash$ db2 CATALOG DB SAMPLE AT NODE airnode
  3. (只适用于 Windows):将数据库编目为系统 ODBC 数据源:
    bash$ db2 CATALOG SYSTEM ODBC DATA SOURCE SAMPLE
                       
  4. 连接该数据库,以确保该连接已正确编目:
    bash$ db2 CONNECT TO SAMPLE USER username USING password 


PHP 社区声称,PHP 可以在生产环境中与 Apache 2 一起使用,只要您使用 prefork MPM 来避免线程安全性问题。这是一条好消息,因为最新的 Linux 发行版本仅配送 Apache 2!


PHP 项目没有提供可在 Linux 上安装的二进制文件,因此为了获得最新的统一 ODBC 支持,您必须下载并编译 PHP 源代码。然而,最新的 Linux 发行版本配送了 Apache 2,因此,这些使用说明针对的是 Apache 2 Web 服务器。要编译 PHP,Linux 发行版本中至少必须包含 apache2-devel(Red Hat Enterprise Linux 4 上的 httpd-devel)、autoconf、automake、bison、flex、gcc 和 libxml2-devel 包。

  1. 要连接 DB2 或 Cloudscape 数据库,Apache 2 Web 服务器必须继承 DB2 客户机实例环境。编辑系统上的 /etc/sysconfig/apache2、/etc/sysconfig/httpd、/etc/init.d/apache2 或 /etc/init.d/httpd,以包含下列行:
    source /home/db2inst1/sqllib/db2profile 
  2. 下载最新 PHP 版本的。本文中用于测试和开发的版本是 PHP 5.0.3。
  3. 从 tarball 提取下列文件:
    bash$ tar xjf php-5.0.3.tar.bz2 
  4. 配置 PHP 源代码以使用 IBM DB2,并指定将为 Apache 2 Web 服务器创建的 PHP 版本:
    bash$ cd php-5.0.3 && ./configure --with-ibm-db2=/home/db2inst1/sqllib 
    --with-apxs2=/usr/sbin/apxs
  5. 构建并安装 PHP:
    bash$ make && su -c 'make install' 

如果一切都顺利,会要求您输入 root 密码,新的 PHP 二进制文件安装在 /usr/local/lib/php/ 目录中。

如果在构建该二进制文件时收到错误消息,那么可能是 Linux 发行版本中缺少必要的开发包,或是指定了错误的 DB2 实例位置。

如果在安装该二进制文件时收到错误消息,那么可能是 PHP 没有理解您所选择发行版本上的 Apache 配置文件。要解决这个问题,则需要修改 Makefile 中的下列代码行,将 install-sapi 移至列表结尾处:



               install_targets = install-sapi install-cli install-pear
install-build install-headers install-programs


               install_targets = install-cli install-pear install-build
install-headers install-programs install-sapi

PHP make install 命令试图代表您更新 Apache 2 配置文件。然而,某些 Linux 发行版本大量修改了这些配置文件,在这种情况下,就需要您的干预。例如,在 SuSE Linux Professional 9.2 上,您必须在 /etc/apache2/conf.d/ 目录中创建一个名为 php5.conf 的新文件,并添加下列行:



               LoadModule php5_module /usr/lib/apache2/libphp5.so
AddType application/x-httpd-php php

现在可以根据您的 Linux 发行版本,通过发出命令 /etc/init.d/apache2 restart/etc/init.d/httpd restart 重新启动 Apache Web 服务器。如果您的 Linux 发行版本提供 SELinux 支持,就需要修改 SELinux 策略,或禁用 SELinux 支持,以便允许 Apache 用户继承实例用户的环境。例如,在 Red Hat Enterprise Linux 4 上,Apache Web 服务器以 apache 用户的身份运行,默认的 SELinux 策略阻止 apache 用户读取 /home/db2inst1/sqllib/db2profile 环境脚本。发出命令 setenforce 0 可以暂时禁用 SELinux,并且允许您重新启动 Apache Web 服务器。


PHP 项目通过提供带完整扩展集的立即部署(ready-to-deploy)的二进制文件,简化了 Windows 开发人员的工作。更妙的是,统一 ODBC 支持被内建在了 PHP 二进制文件中。要在默认安装 Apache 2 的 Windows 上安装 PHP,请执行以下操作:

  1. 下载最新 PHP 版本的 。本文中用于测试和开发的版本是 PHP 5.0.3。请选择 ZIP 文件,而非 Windows installer,因为 ZIP 文件中包含了统一 ODBC 扩展。
  2. 解压这些文件。出于本文的目的,我们假定您将这些文件解压至 C:php 目录。
  3. 将 C:phpphp.ini-recommended 文件复制到 C:php 目录中,将它作为名为 php.ini 的新文件。推荐配置为您的 PHP 环境提供了一套相对安全的默认参数。
  4. 编辑 C:Program FilesApache GroupApache2confhttpd.conf 文件,以便添加下列行:

                           LoadModule php5_module 'c:/php/php5apache2.dll'
    AddType application/x-httpd-php .php
    PHPIniDir 'c:/php'
  5. 重新启动 Apache。


为了演示一些可通过 PHP 和 DB2 执行的基本操作,我们将创建一组 Web 页面来帮助您管理数据库表,该表包含与一组作家有关的数据。首先,我们将创建一个保存作者数据的表,然后编写 PHP 脚本,以便能够浏览现有的作者。最后,我们将编写脚本,使用户可以向该表添加他们自己的作者数据。


为了使用该数据库,我们需要向该表插入一些数据。我们可以发出一些数据操作语言(Data Manipulation Language,DML)语句,但是因为安装了 PHP,所以我们将创建并使用一个简单的 PHP 表单,用它在数据库中插入新记录。下列所有 PHP 脚本都使用了 PHP 文档中所描述的统一 ODBC 函数。

在可以插入数据之前,必须在 PHP 脚本中创建数据库连接。一旦通过返回我们用户名的表列表来确认连接成功,就可以重用我们编写的其余脚本中的连接函数。

使用 PHP 连接数据库的语法如下:

int odbc_connect() (string dsn, string user, string password [, int cursor_type]);

其中:

  • dsn:DB2 目录中注册的数据库名称。
  • user:连接该数据库的用户名。
  • password:用户密码。
  • cursor_type:用于指定游标行为的可选参p。


AUTHOR 表包含 4 列:LAST_NAME、FIRST_NAME、MIDDLE_INITIAL 和 AUTHOR_ID(数据库服务器生成的惟一性标识符,充当该表的主键)。下列 PHP 脚本所包含的数据定义语言(Data Definition Language,DDL)语句将创建该表:


出于两个理由,我们将 SQL 语句定义为字符串变量:
  1. 在文件开始部分定义所有 SQL 语句,使读取代码和修改扭曲应用程序的元素变得更容易。对于包含 JOIN 操作或者包含可能跨越多行的复杂 WHERE 子句的长 SQL 语句,这样做特别有帮助。
  2. 在许多数据库语言程序中,我们需要反复发出相同的语句,有时仅使用稍微不同的值。在开发应用程序时,将语句声明为字符串变量将使之更易于转换成生产应用程序。



               // connect to the database
$conn = odbc_connect('SAMPLE', 'db2inst1', 'ibmdb2');

// define our SQL
$sql = 'CREATE TABLE author (last_name VARCHAR(32) NOT NULL,
   first_name VARCHAR(32) NOT NULL,
   middle_initial VARCHAR(1),
   author_id INTEGER GENERATED ALWAYS AS IDENTITY,
   PRIMARY KEY (author_id))';

// issue our SQL statement directly
odbc_exec($conn, $sql);

// close the database connection
odbc_close($conn);
?>

为了创建该表,要将 PHP 代码复制到一个名为 create_table.php 的文件中。可以继承 DB2 实例环境,从命令行通过发出下列命令来运行该程序:

bash$ source /home/db2inst1/sqllib/db2profile
bash$ php create_table.php

正如您可以看到的,PHP 不仅仅是一种 Web 脚本编制语言 —— 您还可以编写方便的命令行脚本。注意,脚本中暴露了用户名和密码。这不仅是一个相当不安全的实践(如果必须调试脚本,您可能会不经意地就与另一开发人员共享您的密码),而且在必须修改每个脚本中的密码时,这会带来极大的不便。取而代之的是,我们可以用一个公用函数创建模块 db2lib.php,通过用户名‘db2inst1’和密码‘ibmdb2’连接样本数据库 SAMPLE。然后,通过包括 db2lib.php,可以在脚本中使用以下函数:



               
function dbconnect($verbose=FALSE) {
$dbname = "SAMPLE";
$username = "db2inst1";
$password = "ibmdb2";

// odbc_connect returns 0 if the connection attempt fails;
// otherwise it returns a connection ID used by other ODBC functions
$dbconn = odbc_connect($dbname, $username, $password);

if (($verbose == TRUE) && ($dbconn == 0)) {
echo("Connection to database failed.");
$sqlerror = odbc_errormsg($dbconn);
echo($sqlerror);
}

return($dbconn);
}

function my_header($title) {
$html = "
$title

$title




";

return $html;
}
?>

注意,如果没有为 dbconnect()$verbose 参数指定任何值,则使用默认值 FALSE。如果该脚本成功地连接到数据库,那么只需向调用者返回数据库连接句柄。

如果连接失败,并且已经要求了 verbose 输出,那么脚本会告诉您它无法连接到数据库,并调用 odbc_errormsg() 函数来输出数据库所返回的错误消息。

在名为 db2lib.php 的文件中保存代码,以便能够在其余脚本中包含该函数。


在大多数情况下,应该像建立到数据库的连接中所描述的那样编目数据库连接,以便可以从命令行测试该连接。不过,也可以使用 IBM DB2 ODBC 驱动程序连接字符串来连接未编目的 DB2 或 Cloudscape 数据库。为了使用驱动程序连接字符串进行连接,需要将下列字符串传递给 odbc_connect() 方法的 DSN 参数,并用正确的主机名、数据库名和端口号进行替换:

DRIVER={IBM DB2 ODBC DRIVER};HOSTNAME=localhost;DATABASE=dbname;PROTOCOL=TCPIP;PORT=1527; 


一旦成功连接到数据库,就可以开始进行一些有趣的工作,例如插入、更新和检索数据。可以使用 odbc_exec() 函数发出简单的 SQL 语句(没有包含用于变量输出的参数标志的语句)。下列脚本将向 AUTHOR 插入新的行:



               // include the dbconnect() and my_header() functions
include_once("db2lib.php");

echo(my_header"INSERT data into AUTHOR table");

$author_insert = "INSERT INTO author" .
"(last_name, first_name, middle_initial)" .
"VALUES('Scott', 'Daniel', 'B')";

$verbose = TRUE;
$dbconn = dbconnect($verbose);

if ($dbconn != 0) {
// odbc_exec returns 0 if the statement fails; otherwise
// it returns a result set ID
$result = odbc_exec($dbconn, $author_insert);

if ($result == 0) {
echo("INSERT statement failed.");
$sqlerror = odbc_errormsg($dbconn);
echo($sqlerror);
}
else {
echo("Successfully inserted one row.");
}
}
else {
echo("

Connection failed.

");
}
echo("");
?>



第二个过错就是假定将打开 register_globals php.ini 指令。该指令所带来的结果是在给定 PHP 脚本的全局范围内自动将 CGI 参数变成具有相同名称的 PHP 变量。虽然这是一个很方便的功能,但它也是极其不安全的,在我的文章发表不久之后,PHP 开发人员就明智地选择默认关闭 register_globals。当然,这就破坏了那篇文章中的许多示例。我保证这篇 文章中的示例是可以工作的!

当然在现实世界中,您可能不会通过编写脚本来向数据库重复插入同一行。您可能编写一个脚本,提供简化数据输入工作(或 Web 站点用户的数据输入工作)的 HTML 表单。幸运的是,PHP 使处理 HTML 表单输入变得更加容易。当 PHP 脚本是某个 HTML

元素的目标动作时,CGI 参数就自动转换成可在 PHP 脚本内使用的 $_GET[]$_POST[] superglobal 数组的成员。保存 CGI 参数的数组是由 元素中 action 属性的值决定的。superglobal 数组将根据表单中各个输入字段的 NAME 属性,来包含带有指定 CGI 参数值的字段。

例如,由 组成的表单将在 $_POST[] superglobal 中创建 submit.php 脚本中名为‘myparm’和值为‘BLUE’的字段。您可以使用 $_POST['myparm'] 访问该值。

如果需要多次发出同一语句,odbc_exec() 函数是一种代价高昂的方法,因为它每次都要准备和执行这条语句。您可以通过使用 odbc_prepare() 在单独的步骤中准备这条语句,并通过多次调用 odbc_execute() 执行准备好的语句,从而提高应用程序的性能。

在下列例子中,我们创建另一个 PHP 模块和脚本,用它们显示 HTML 表单,帮助我们向 AUTHOR 表插入数据。该模块演示了如何使用 odbc_prepare() 函数准备包含参数标志的 INSERT 语句。我们使用 PHP 的功能强大的 HTML 表单解析来确定是否可以使用任意 CGI 变量值调用脚本;如果不是,则只返回这个 HTML 表单。如果可以用 CGI 变量值调用脚本,则解析表单字段的内容,并将参数作为数组传递给 odbc_execute() 函数,以便向该表插入数据。



               function insert_data( $dbconn, $last_name, $first_name, $middle_initial) {
$insert_stmt = "INSERT INTO author " .
"(last_name, first_name, middle_initial) VALUES(?, ?, ?)";

// odbc_exec returns 0 if the statement fails; otherwise
// it returns a result set ID
$result = odbc_prepare($dbconn, $insert_stmt);

if ($result == 0) {
echo("odbc_prepare() failed.");
return(FALSE);
}
else {
odbc_execute($result, array($last_name, $first_name, $middle_initial));
echo("Inserted $last_name and $first_name and $middle_initial");
}
return(TRUE);
}

function author_form($action) {
// $form is a HERE string that spans multiple lines
$form = <<


Last name:
First name:
Initial:



HERE;

return($form);
}
?>

db2form.php 是我们的第二个 PHP 模块,包含 insert_data()author_form() 函数。将这些函数置于一个模块中有助于我们在其他脚本中重用它们。这正是脚本 prepinsert.php 所起的作用;它包含 db2lib.php,以便能够利用标准数据库连接函数,此外还包含 db2form.php,以便能够利用标准的 INSERT 和表单提交函数。该脚本返回一个 Web 页面,页面中包含允许我们在数据库中插入新行的简单表单,实际上这个脚本就是一个调用模块函数的包装器。


最后一个错误,也是最严重的错误就是没有提醒开发人员净化输入和输出数据的重要性。PHP Security Consortium 提供了许多关于该主题和许多更高级安全性主题的免费高质量参考资料。让我们这样说好了,如果没有确保脚本中所使用的数据是安全的,您的服务器成为恶意团体的牺牲品,不必对此感到惊讶。

该脚本的一个重要方面就是用来返回净化后 CGI 输入数据的 preg_replace() 调用集合。滥用 Web 应用程序的常见方法就是蓄意向表单变量传递恶意输入;如果 Web 应用程序简单地使用原始输入、将数据传递给 SQL 语句、用恶意输入作为文件名,或者不信任输入数据,那么可能发生意想不到的结果。在这个脚本中,在将输入字符串值插入数据库之前,我们使用 Perl 兼容的正则表达式函数 preg_replace() 来确保该输入字符串只包含字母字符。



               /* The form within this PHP script calls itself as
the action-handler using the $_SERVER['PHP_SELF']
superglobal variable.

If the script is called without CGI parameter values,
only the form is displayed on the page.

If the script is called with CGI parameter values,
we also call the custom function insert_data(). */

// include our custom function libraries
include_once("db2lib.php");
include_once("db2form.php");

echo(my_header("INSERT using odbc_prepare"));

$first_name = '';
$last_name = '';
$middle_initial = null;

// cleanse CGI variable input; allow names with alphabetic chars only
$name_pattern = '/^s*([a-z]+)s*$/i';
$name_replacement = '$1';

if (array_key_exists('first', $_POST) && array_key_exists('last', $_POST)) {
   $first_name = preg_replace($name_pattern, $name_replacement, $_POST['first']);
   $last_name = preg_replace($name_pattern, $name_replacement, $_POST['last']);
   $middle_initial = preg_replace($name_pattern, $name_replacement, $_POST['middle']);
}

if (($last_name != '') && ($first_name != '')) {

   if ($middle_initial == '') {
       // insert a true NULL value
       $middle_initial = null;
   }
   $verbose = TRUE;
   $dbconn = dbconnect($verbose);

   if ($dbconn != 0) {
       insert_data($dbconn, $last_name, $first_name, $middle_initial);
       // always close your database connection
       odbc_close($dbconn);
   }
}

// $_SERVER['PHP_SELF'] is a superglobal variable that contains the name of this script
echo(author_form($_SERVER['PHP_SELF']));
echo('');
?>


SELECT 语句通常返回多行数据。当您为 SELECT 语句调用 odbc_exec() 函数时,该函数返回一个结果集标识符。结果集是一个数组,包含匹配数据库查询的零行或多行;结果集标识符就是一个值,您需要将该值传递给其他函数,以便使用结果集中的行。

一旦检索到结果集标识符,就可以用多种方式检索结果集的内容。最为方便的方法之一就是迭代 odbc_fetch_array() 函数,如下面例子所示:


PHP 应用程序中的普遍使用模式就是发出 SELECT 语句,使用 odbc_num_rows() 计算所返回的行数,并且只在该数字 > 0 时处理结果集。



                    $select_stmt = 'SELECT last_name FROM author';
$result = odbc_exec($dbconn, $select_stmt);
if (odbc_num_rows($result) > 0) {
while ($row = odbc_fetch_array($result)) {
// do stuff
}
}
else {
print "No results found.";
}

然而,您还不如使用 odbc_fetch_array() 作为条件语句,输入一个 while 循环;如果没有要读取的行,while 循环不会进行任何迭代。如果需要输出概要消息,说明获取了多少行,或说明没有找到任何结果,则需要将计数器变量初始化为 0,并在 while 循环中逐步递增该变量。



                    $select_stmt = 'SELECT last_name FROM author';
$result = odbc_exec($dbconn, $select_stmt);
$i = 0;
while ($row = odbc_fetch_array($result)) {
// do stuff
$i++;
}
if ($i == 0) {
print "No results found.";
}

在 Cloudscape 和 DB2 数据库中,当为统一 ODBC 实现性能变通方法时,推荐实践也具有一定的执行优势。odbc_num_rows 将不从带有只向前移动游标的 SELECT 语句中返回有用结果。



               // include our custom function libraries
include_once("db2lib.php");
include_once("db2form.php");

echo(my_header('Simple SELECT statement'));

function display_authors($dbconn) {
// select all rows from the AUTHOR table
$select_stmt = 'SELECT last_name, first_name, middle_initial, author_id
FROM author';

if ($dbconn != 0) {
// odbc_exec returns 0 if the statement fails;
// otherwise it returns a result set ID
$result = odbc_exec($dbconn, $select_stmt);

if ($result == 0) {
echo("SELECT statement failed.");
$sqlerror = odbc_errormsg($dbconn);
echo($sqlerror);
}
else {
print '
';
while ($row = odbc_fetch_array($result)) {
print '';
print '';
print '';
print '';
}
print '
LastFirstInitialID
' . $row['LAST_NAME'] . '' . $row['FIRST_NAME'] . '' . $row['MIDDLE_INITIAL'] . '' . $row['AUTHOR_ID'] . '
';
}
}
}

$verbose = TRUE;
$dbconn = dbconnect($verbose);

display_authors($dbconn);

echo('');

// always close your database connection
odbc_close($dbconn);
?>

db2select.php 定义了一个名为 display_authors() 的新函数;我们通常将该函数添加给一个现有模块,但是出于本文的目的,我们将添加它,将它内联于页面。该函数通过在 while() 循环中迭代 odbc_fetch_array() 函数,来显示 AUTHOR 表中所有作者的列表。每次评估 while() 条件时,odbc_fetch_array() 就返回一个名为 $row 的数组变量,表示所需的行。该数组的字段是命名字段,用于映射 SELECT 语句中需要的大写体列名。当不再需要从结果集读取行时,odbc_fetch_array() 会返回 FALSE,且 while() 循环结束。


多年以来,虽然 PHP 的统一 ODBC 扩展一直得到发展、维护和积极使用,但在一些领域中,它与 Cloudscape 和 DB2 的匹配仍然不够完美。这不是统一 ODBC 扩展的责任:就其本质而言,它必须支持许多异构数据库服务器,但是并非每一种数据库客户机都像 DB2 客户机那样密切支持 ODBC 规范。在这一小节中,您将学习如何克服 Cloudscape 和 DB2 开发人员在使用统一 ODBC 扩展创建 PHP 应用程序时碰到的一些常见问题。


在使用统一 ODBC 扩展连接 Cloudscape 和 DB2 数据库时,最常提出的问题之一就是发现默认情况下,远程客户机到服务器的性能很差。开发人员经常在 PHP、DB2 客户机和数据库服务器都位于同一台机器的情况下构建和测试其应用程序,在这种配置中,该组合表现极佳。然而,只要 PHP 和 DB2 客户机运行在不同于数据库服务器的机器上,性能就会明显下降。

产生这种性能问题的原因是,统一 ODBC 使用可滚动的键集驱动游标来发出 SELECT 语句。不幸的是,Cloudscape 和 DB2 数据库服务器都要被迫模拟这种游标,结果导致从服务器读取每一行消费掉了大量网络带宽。为了避免网络开销和性能下降,您可以通过从 DB2 命令行发出下列命令,强制 DB2 客户机自动替换只能向前移动的游标(其中,dbname 表示数据库名称):

bash$ db2 UPDATE CLI CFG FOR SECTION dbname USING PATCH2 6 

在不这样设置的情况下,获取每行包含三个 VARCHAR 列的 10,000 个行所需的平均时间为 22 秒。而在应用该设置的情况下,同一测试平均花费 2 秒!

注意,该设置 修改一些统一 ODBC 函数的行为;odbc_num_rows 将不反映 SELECT 语句所返回的行数,而 odbc_fetch_* 函数将总是从结果集中读取下一行。


统一 ODBC API 不提供对存储过程 INOUT 和 OUT 参数的支持。如果必须使用 INOUT 和 OUT 参数,可以获取为 DB2 数据库提供的变通方法。SQL 存储过程的创建十分容易,因此,对于每个带有 INOUT 和 OUT 参数的存储过程,都可以创建一个包装器存储过程。包装器存储过程仅仅调用现有的存储过程,并将所包装存储过程的 OUT 和 INOUT 参数作为包装器存储过程的结果集返回。

在下列例子中,我们面临着调用名为 OUT_LANGUAGE 的存储过程的挑战,该存储过程返回一个名为 OUT_LANGUAGE 的 OUT 参数。我们发出 CREATE PROCEDURE 语句来创建 SQL 存储过程 WRAP_OUT。这个新的存储过程将 OUT_LANGUAGE 参数作为单行结果集中的一列返回。



               CREATE PROCEDURE wrap_out()
DYNAMIC RESULT SETS 1
LANGUAGE SQL
BEGIN
DECLARE lang CHAR(8);
DECLARE c1 CURSOR WITH RETURN TO CLIENT FOR
SELECT CONCAT('', lang) FROM SYSIBM.SYSDUMMY1;
CALL OUT_LANGUAGE(lang);
OPEN c1;
END @

为了从 PHP 应用程序中调用 WRAP_OUT 存储过程,需要使用 odbc_exec() 发出 CALL WRAP_OUT() 语句,并且可以像从 SELECT 语句中读取行那样来读取这一行。


特别感谢统一 ODBC 扩展的作者和维护者,是他们使本文的发表成为可能:Stig Bakken、Andreas Karajannis、Frank M. Kromann 和 Dan Kalowsky。还要感谢 George Schlossnagle 提供了有帮助的反馈。

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • 是 PHP 开发社区的主页,您可以从中找到 PHP 源代码和二进制文件下载、PHP 手册(包括用户注解),以及 和 PHP 语言扩展存储库,并且可以将它们用于您自己的应用程序。

  • 将 PHP 应用程序连接到 Apache Derby 重点介绍了如何在 Windows 上使用 PHP 和统一 ODBC 扩展来连接 IBM Cloudscape 和 Apache Derby 数据库。

  • Application Development Experiences with PHP and IBM DB2 Universal Database Version 8 提供了对统一 ODBC 扩展的深入研究,并提供了一些源代码补丁,以改进 BLOB 支持。

  • 提供了用于 DB2 Universal Database for Linux, UNIX and Windows 产品的官方文档和示例的完整集合。

  • 请在 DB2 UDB and Cloudscape Open Source 编程语言论坛 与作者和其他读者分享您的问题以及您对这篇文章和 PHP 的看法。

  • 关于最新的 Linux(WebSphere Studio Application Developer、WebSphere Application Server、DB2 Universal Database、Tivoli Access Manager 和 Tivoli Directory Server)免费试用软件下载,以及 how-to 文章和技术支持,请参阅 Speed-start your Linux app 站点。

  • 通过参与 developerWorks blogs 加入 developerWorks 社区。

  • 购买 Developer Bookstore 的 DB2 专区中 打折出售的 DB2 书籍

下载

描述 Name Size Download method
PHP sample code used in this article php_samples.zip 10 KB
*关于下载方法的信息
关于作者
Author photo自 1998 年以来,Dan Scott 就一直在应用程序开发领域工作,从事 IBM DB2 通用数据库方面的研究。他撰写过文章,出版过书籍,并参加了与使用 PHP 和其他脚本编制语言创建 IBM 数据库相关的主题会议。Dan 喜爱摩托车、山地车、音乐、推理小说以及新研磨咖啡的芳香。他目前与其未婚妻和猫一起居住在多伦多。

阅读(794) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~