PDO
本词条缺少
信息栏
,补充相关内容使词条更完整,还能快速升级,赶紧来
编辑
吧!
PDO
一是
PHP
数据对象(
PHP Data Object
)的缩写。二是
丙二醇
简称PDO
。三是阿曼石油开发公司 (
petroleum development of Oman.
)的缩写,四是过程数据对象(
Process data object
)的缩写。五是太平洋十年涛动
目录
PHP数据对象
PDO
并不提供数据库抽象,它并不会重写
SQL
或提供数据库本身缺失的功能,如果你需要这种功能,你需要使用一个更加成熟的抽象层。
PDO
需要
PHP5
核心
OO
特性的支持,所以它无法运行于之前的
PHP
版本。
运行
在Unix
环境下
PHP5.1
以上版本中:
如果你正在使用PHP5.1
版本,
PDO
和
PDO SQLITE
已经包含在了此发行版中;当你运行
configure
时它将自动启用。推荐你将
PDO
作为共享扩展构建,这样可以使你获得通过
PECL
升级的好处。推荐的构建支持
PDO
的
PHP
的
configure line
应该也要启用
zlib
。你也应该启用你选择的数据库的
PDO
驱动 ;关于这个的更多信息请查看
database-specific PDO drivers
,但要注意如果你将
PDO
作为一个共享扩展构建,你必须也要将
PDO
驱动构建为共享扩展。
SQLite
扩展依赖于
PDO
,所以如果
PDO
作为共享扩展构建,
SQLite
也应当这样构建
./configure --with-zlib --enable-pdo=shared --with-pdo-sqlite=shared --with-sqlite=shared
将PDO
安装为一个共享模块后,你必须编辑
php.ini
文件使得在
PHP
运行时自动载入
PDO
扩展。你同样需要启用那儿的特定数据库驱动;确保他们列出在
pdo. so
行之后,因为
PDO
必须在特定数据库驱动载入之前初始化。如果你是以静态方式构建的
PDO
和特定数据库驱动扩展,你可以跳过这一步。
extension=pdo. so
让PDO
作为一个共享的模块将使你可以在新版
PDO
发布时运行
pecl upgrade pdo
命令升级,而不用强制你重新构建整个
PHP
。注意如果你是这样做的,你也需要同时升级你的特定数据库驱动。
在windows
环境下
PHP5.1
以上版本中:
PDO
和主要数据库的驱动同
PHP
一起作为扩展发布,要激活它们只需简单的编辑
php.ini
文件:
extension=php_pdo.dll
然后,选择针对特定数据库的DLL
文件使用
dl()
在运行时加载,或者在
php.ini
文件中
php_pdo.dll
行后启用它们,如:
extension=php_pdo.dll
extension=php_pdo_firebird.dll
extension=php_pdo_informix.dll
extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
extension=php_pdo_oci.dll
extension=php_pdo_oci8.dll
extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll
这些DLL
文件应当存在于系统的
extension_dir
目录里。
注意 PDO_INFORMIX
只能作为一个
PECL
扩展使用。
========================================================================================
PHP 5.1
发布时将附带一个全新的数据库连接层,即
PHP Data Objects (PDO)
。虽然
PHP
一直都拥有很好的数据库连接,但
PDO
让
PHP
达到一个新的高度。学习如何获得、安装和使用
PDO
,以连接到
IBM? DB2? Universal Database?
和
IBM Cloudscape?
数据库,插入和检索数据,并探索更多高级特性,例如预处理语句(
prepared statements
)、绑定参数(
bound parameters
)、可滚动游标(
scrollable cursors
)、
定位更新
(positioned updates
)以及
LOB
。
背景
随着拥有更成熟 OO
语法的
PHP 5
的发布,
PHP
越来越多地受到越来越大的机构的关注,对于
PHP
来说,提供更加一致的和可访问的数据访问
API
变得越来越重要。
PHP
与流行的
开放源代码关系数据库管理系统
(RDBMS
)
MySQL
之间总是很有默契。这对拍档的成功很大程度上是由于它们免费可用,而且进入的门槛也比较低,这两种产品的合作使它们各自都取得了广受推崇的地位。
很多 PHP
应用程序开发人员都习惯于
PHP-MySQL
这对组合,以致
PHP
对其他数据库的支持常常模仿
MySQL
客户机库
API
。然而,并不是所有的数据库客户机
API
都是一样的,也不是所有的数据库都提供相同的特性。虽然存在模仿,但不同的
PHP
数据库扩展都有它们各自的怪僻和不同之处,所以从一种数据库迁移到另一种数据库时会有一些困难。虽然这不是创建
PDO
的直接原因,但是在设计过程中还是有一定影响的。
如果您是带着想结合使用 PHP
和
DB2
的目的阅读本文,那么您很可能属于以下类型中的一种:
您从一家小公司开始,在 MySQL
(举个例子)上运行
PHP
,由于业务增长,您需要
DB2
所提供的可伸缩性
/
可靠性
/
支持或其他特性。您希望移植代码,以使用
DB2
,但由于
API
的变化,您需要编写或实现一个抽象层,以便在
DB2
上测试应用程序的同时可以继续在旧的数据库上运行。不仅如此,您还希望能有自己的选择,并保留支持其他
RDBMS
的可能性,因为您清楚,有些客户机可能已经和其他平台栓在一起了。
您用 PHP
在
MySQL
之上构建了一个小型的部门应用程序(同样,这只是举个例子,我并不是要跟
MySQL
过不去)。事实证明这个应用程序本身很有用,现在已经在这个部门之外使用,并且闯入了
CIO/CTO
的法眼
——
现在需要遵从托管的标准数据库。(是的,这是第一点的一个变种。) 在其他某些复杂的企业级应用程序的后台,您已经有一个
DB2
实例,您希望利用
PHP
的快速应用程序开发和
原型设计
来生成动态报告。
目标
至此我们已经掌握了数据库及 PHP
的背景知识,现在正好可以提及
PDO
背后的一些设计目标:
为大多数数据库 API
中的常见特性提供一致的
API
。
具有可扩展性,以使数据库供应商 X
仍然可以暴露特性
Y
并保持
PDO
的兼容性。
提供大量基本的兼容性技巧,以便能够更方便地创建跨数据库兼容的应用程序。
不为给定数据库 API
中本来没有的特性(例如序列)提供完全抽象或仿真。
PDO
类意图为您提供对数据库本地特性的一致性访问,并减少干扰。
通过将与 PHP
内部打交道的代码(这是最难于编写的部分)集中起来,简化
PHP
数据库驱动程序的创建。
最后一点非常重要。PDO
是模块化结构,它被分成一个公共核心以及一个或多个驱动程序扩展,公共核心提供了在脚本(
PDO
本身)中使用的
API
,驱动程序扩展则为
PDO
和本地
RDBMS
客户机
API
库架起一座桥梁。
DB2
用户将会希望使用
PDO_ODBC
驱动程序,据称它可以提供以下特性:
它经过重新编写,能支持遵从 ODBC V3
的驱动程序和驱动程序管理器。它还考虑了对
DB2
特定特性和优化的支持,这成为设计过程中的一部分
——
不是后来补充的。
对于取 10,000
行记录这样的
DB2
访问操作,使用
PDO_ODBC
驱动程序时的性能比使用传统的
PHP Unified ODBC
扩展要快大约
10
倍。之所以有这么大的差异,是因为在
PDO
中默认的
游标
是轻量级的只能向前移动的游标。
获取
PHP 5.1
发布时将附带
PDO
,但是也可以通过
PECL
这个
PHP
扩展库(
PHP Extension Repository
)来结合使用
PDO
和
PHP 5.0.3
及以上版本。如果您使用的是
Windows
,那么您会欣喜地发现安装过程要简单得多。
我将假设您已经拥有配置 PHP 5
使之使用您选择的
Web
服务器的经验,只有在此假设下,我才能集中精力关注更相关的细节。同样,我还将假设您使用的是一个
DB2 Universal Database
服务器或
网络服务器
模式下的 IBM Cloudscape
数据库,并且接受了用户为
db2inst1
、密码为
ibmdb2
的默认安装选项。如果您自己编译驱动程序,那么在进行编译的机器上,应该安装有
DB2
客户机,并且存在应用程序开发
header
,否则编译将遭到失败。
安装
在 PHP 5.0.3
及以上版本上通过
PECL
进行安装
默认情况下,PHP
将安装
"PEAR"
包管理系统。您选择的
OS
发行版很可能已经创建了一个包含
PEAR
的组件的包,很可能您已经安装了这个包,并准备运行它。让我们试验一下。
如果它不能工作如果您没能看到类似于左侧文本的输出,那么很可能您没有安装需要的所有包。这时应查阅您选择的 OS 发行版的文档,看看接下来应该做什么。或者,您可以自己编译 PHP。 |
清单 1.
列出已安装的
PEAR
包
$ pear listInstalled packages:=================== Package Version StateArchive_Tar 1.1 stable Console_Getopt 1.2 stablePEAR 1.3.4 stable XML_RPC 1.1.0 stable |
这个包列表表明,我已经安装了 PEAR 1.3.4
。很可能您也会安装那个版本。为了成功地安装
PDO
,需要升级到
PEAR 1.3.5
;这个过程很快,很顺利:
$ sudo pear upgrade PEAR |
现在便可以放心安装 PDO
了:
$ sudo pear install PDO |
您已经安装了 PDO
核心,为了使之生效,需要在
php.ini
文件中启用它。您需要添加以下一行:
extension=pdo. so |
现在安装用于 PDO
的
ODBC
驱动程序,如果您需要连接到
DB2
、
Cloudscape
或
Apache Derby
,就需要这个驱动程序:
$ sudo pear install PDO_ODBC |
您将看到这样的提示:flavour,dir ? (just leave blank for help)
。这是一个稍微有点隐蔽的提示,它询问需要配置哪种类型的
ODBC
驱动程序,以及将它安装在哪里。如果在安装
DB2
时选择了默认安装选项,那么可以输入
ibm-db2
,这相当于
ibm-db2,/home/db2inst1/sqllib
。如果您选择了不同的安装位置,那么应该用它置替换
/home/db2inst1/sqllib
。输入了正确的细节后,按下
enter
键,这样驱动程序就会构建和安装。
您需要将驱动程序添加到 php.ini
文件中,从而激活驱动程序。确保将下面这一行添加在之前所添加的
pdo. so
这一行
之后
,否则 PHP
不能正确地初始化。
extension=pdo_odbc.so |
PHP 5.1
及以上版本上的安装
PHP 5.1
发布时附带了
PDO
。为获得
DB2
支持,只需将下面的开关添加到配置行。您显然希望添加更多的配置选项,以满足您自己的
Web
服务器。关于这方面的详细内容,可以查看
PHP
文档。我将假设您使用的是一个最近的
Linux?
发行版,并运行
Apache 2:
$ tar xjf php-5.1.0.tar.bz2$ cd php-5.1.0 $ ./configure --with-pdo-odbc=ibm-db2,/home/db2inst1/sqllib \ --with-apxs2=/usr/sbin/apxs$ make $ sudo make install |
Windows
上的安装
Windows
上的安装比起
UNIX?
上的安装来要简单一些。如果您下载了
PHP 5.1
,那么就已经拥有了这个包中相关的
DLL
。否则,您需要从
PHP
快照站点(请参阅本文后面的
“
下载
”
小节)下载这些
DLL
。
为了激活 PDO
,将下面两行添加到
php.ini
文件:
extension=php_pdo.dll extension=php_pdo_odbc.dll |
重新启动 Web
服务器
安装完毕后,应该完全重新启动 Web
服务器,以确保
PHP
装载新的扩展,这样就可以开始使用
PDO
了。如果您使用的是
UNIX
平台,那么需要获得
DB2
客户机的
DB2
实例环境,以便正确地初始化。如果您使用的是
bourne shell
型的
shell
,那么可以通过
运行命令
. /home/db2inst1/sqllib/db2profile
来获得。(注意
:
假定开头部分的句号已经在那里!)您需要作出安排,使之在
Web
服务器启动脚本中自动发生。
关键概念
1
、连接管理
连接是通过创建 PDO
基类的实例而建立的。不管您想要使用哪种驱动程序,您总是使用
PDO
类名。
构造函数
接受用于指定
数据源
(即 DSN
)的参数,可能还包括用户名和密码参数(如果有的话)。最后一个参数用于传递附加的调优参数到
PDO
或底层驱动程序
——
后面很快会有更详细的论述。下面是一个简短的连接到
DB2
的示例脚本:
清单 2.
如何使用
PDO
连接到
DB2
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); echo "Connected\n";} catch (Exception $e) { echo "Failed: " . $e->getMessage();} |
odbc:SAMPLE
告诉
PDO
它应该使用
ODBC
驱动程序,并且应该使用
"SAMPLE"
数据库。如果使用一个驱动程序管理器,那么可以用一个
ODBC
级
数据源
名称替代 SAMPLE
。实际上,在冒号
字符
之后可以指定任何有效的 ODBC
数据源连接字符串。
如果连接成功,您将看到消息 "Connected"
,否则,
PDO
将抛出一个
PDOException
,解释为什么连接失败。可能的原因包括无效的参数,不正确的用户
/
密码,甚至是您忘了装载驱动程序。
值得注意的是,除非您捕捉从
构造函数
抛出的异常,否则,如果 PHP
脚本未能连接到数据库,它将终止。这与传统的
PHP
数据库扩展有很大的不同。对于不喜欢异常的人来说,只有两个
“
硬故障(
hard-failure
)
”
点可能
抛出异常
,这是其中一个点(另一个地点是,当您试图使用
事务
时缺乏对事务的支持)。对于所有其他错误,PDO
将使用您选择的 错误处理设置。
连接将保持开放状态,直到所有对它的引用被释放。如果在主脚本的顶端打开连接,并将其句柄存储在一个
全局变量
中,那么该连接将一直处于开放状态,直到脚本结束,或者直到 $dbh
变量被设为
null
。如果在一个函数中打开连接,并且只将句柄存储在一个本地变量中,那么当函数返回时,连接将被关闭。这些语义对于
PHP
中的任何对象都是一样的,没有什么特别的地方。
ODBC 连接池如果您使用的是 Windows,或者如果您选择在 UNIX 型平台上使用一个 ODBC 驱动程序管理器,那么值得注意的是,PDO_ODBC 将自动尝试使用该驱动程序管理器的 ODBC 连接池特性。这个特性类似于 PHP 级连接缓存,不要求专门请求一个持久的连接。此外,缓存是在 ODBC 级进行的,这意味着在同一个进程中运行的其他组件(例如在 IIS 下运行的 ASP/.Net 脚本)也能利用相同的连接池。 |
对于流量较大的站点,让 PHP
在不同请求的间隙中缓存打开的连接,使得每个进程(每个惟一的连接参数集)只需花费一次建立连接的成本,这样做常常很有益处。虽然这听起来像是一个不错的想法,但您应该仔细评估这样做对系统的影响,因为当大量缓存的连接空闲在那里的时候,就会适得其反。
要建立一个缓存的连接(如果您更熟悉传统的数据库扩展的话,也可以说是 *pconnect()
),需要在实例化数据库连接时传递一个属性:
清单 3.
如何用
PDO
连接到
DB2
,使用持久(缓存)连接
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO_ATTR_PERSISTENT => true)); echo "Connected\n"; } catch (Exception $e) { echo "Failed: " . $e->getMessage(); } |
2
、事务提交
至此,您已经通过 PDO
连接到了
DB2
,在发出查询之前,您应该理解
PDO
是如何管理
事务
的。如果之前没有接触过事务,那么首先要知道事务的 4
个特征:原子性(
Atomicity
)、一致性(
Consistency
)、独立性(
Isolation
)和持久性(
Durability
),即
ACID
。用外行人的话说,对于在一个事务中执行的任何工作,即使它是分阶段执行的,也一定可以保证该工作会安全地应用于数据库,并且在工作被提交时,不会受到来自其他连接的影响。事务性工作可以根据请求自动撤销(假设您还没有提交它),这使得脚本中的错误处理变得更加容易。
警告只有在通过 PDO::beginTransaction() 启动事务的情况下,才会发生自动回滚。如果手动地发出开始一个事务的查询,那么 PDO 就无法知道该事务,从而不能在必要时进行回滚。 |
不幸的是,并不是每种数据库都支持事务,所以当第一次打开连接时,PDO
需要在所谓的
“
自动提交(
auto-commit
)
”
模式下运行。自动提交模式意味着,如果数据库支持
事务
,那么您所运行的每一个查询都有它自己的隐式事务,如果数据库不支持事务,每个查询就没有这样的事务。如果您需要一个事务,那么必须使用 PDO::beginTransaction()
方法来启动一个事务。如果底层驱动程序不支持事务,那么将会抛出一个
PDOException
(无论错误处理设置是怎样的:这总是一个严重错误状态)。在一个事务中,可以使用
PDO::commit()
或
PDO::rollBack()
来结束该事务,这取决于事务中运行的代码是否成功。
DB2 特性虽然我认为事务通常要更快一些,但您还是应该自己评估事务是否真的可以加快代码。例如,在高并发环境中您可能会发现,过度使用事务会增加锁开销。如果在应用程序中出现这种情况,那么建议的补救办法是在一般情况下使用自动提交,而对于真正需要全部 ACID 特征的代码部分则仍然使用事务。 |
当脚本结束时,或者当一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO
将自动
回滚
该事务。这是一种安全措施,有助于避免在脚本非正常结束时出现不一致的情况 ——
如果没有显式地提交事务,那么假设有
某个地方
会出现不一致,所以要执行回滚,以保证数据的安全性。
清单 4.
在事务中执行批处理
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO_ATTR_PERSISTENT => true)); echo "Connected\n"; $dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION); $dbh->beginTransaction(); $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')"); $dbh->exec("insert into salarychange (id, amount, changedate) values (23, 50000, NOW())"); $dbh->commit(); } catch (Exception $e) { $dbh->rollBack(); echo "Failed: " . $e->getMessage(); } |
在上面的示例中,假设我们为一个新雇员创建一组条目,这个雇员有一个 ID
号,即
23
。除了输入这个人的基本数据外,我们还需要记录雇员的薪水。两个更新分别完成起来很简单,但通过将这两个更新包括在
beginTransaction()
和
commit()
调用中,就可以保证在更改完成之前,其他人无法看到更改。如果发生了错误,
catch
块可以
回滚事务
开始以来发生的所有更改,并打印出一条错误消息。
并不是一定要在事务中作出更新。您也可以发出复杂的查询来提取数据,还可以使用那种信息构建更多的更新和查询。当事务在活动时,可以保证其他人在工作进行当中无法作出更改。事实上,这不是 100%
的正确,但如果您之前没有听说过事务的话,这样介绍也未尝不可。
关于 PHP
应用程序中安全性的说明
PHP Security Consortium 虽然本文表明在使用 PDO 时不再需要引用输入,但这不是说您应该盲目地使数据通过数据库。XSS 攻击是很实际的危险。您应该总是确保对传入应用程序的不受信任的数据应用适当的过滤器,并采取措施避免让不受信任的数据在站点上发出 HTML 或 javascript。 请访问 The PHP Security Consortium 以了解关于这些危险的更多知识,以及应该如何避免这些危险。 |
很多 PHP
脚本中一个常见的缺陷是缺乏输入检验。这种缺陷可以被利用,从而招致
XSS
(
Cross Site Scripting
)以及
SQL
入侵攻击。在
SQL
入侵中,不受信任的数据(例如发给
Web
网页的反馈)和其他文本被衔接在一起,构成一个查询。攻击者可以蓄意地安排他们的输入,使之溢出引号之外,并在您想运行的真正查询后面链接上任意一个查询。这种攻击使攻击者可以更新、插入或删除数据,甚至可能可以看到数据库中的任意信息。
XSS
也是一个类似的问题。不过这一次不受信任的数据瞄准的是浏览站点的人们,而不是应用程序本身。通过提交包含
HTML
或
javascript
组合的文本,攻击者期望您之后会将那种数据直接输出到其他访问站点的人那里,从而使
恶意代码
可以在站点访问者的浏览器上运行。
在编写应用程序时,需要同时考虑这两种攻击。如果小心地检验和过滤输入,这两种攻击都是可以防止的。对 XSS
的处理很有技巧性,所以在这里我不便多讲(不过可以从侧栏找到有用的参考资料)。相比之下,
SQL
入侵更容易对付。您只需在构造查询之前,适当地排除每块不受信任的数据。这种事情有点烦杂,特别是当您有大量的字段要处理时,很容易忘记做这件事。
虽然这是有用的(并且也是重要的)信息,但是您可能想知道,为什么我要花时间提到这一点,本文的重点不是结合使用 PDO
和
DB2
吗?原因是这样的:
PHP
现在得到很广泛的部署,自然地,大量流行的基于
PHP
的应用程序也得到了广泛的部署。每当某一种这样的应用程序(和
PHP
本身没有联系)被发现存在漏洞时,
PHP
常常被误认为是不安全的,可被利用的或者有缺陷的。为了避免将来出现这样的情况,我们可以采取的一个措施是鼓励应用程序开发人员多考虑安全问题,从而减少由诚实的错误导致的损害。扯远了,下面继续介绍其他关键概念。
3
、预处理过程
很多更成熟的数据库都支持预处理语句的概念。什么是预处理语句?您可以把预处理语句看作您想要运行的 SQL
的一种编译过的模板,它可以使用变量参数进行定制。预处理语句可以带来两大好处:
查询只需解析(或准备)一次,但是可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查询,这个过程要花比较长的时间,如果您需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/
编译
/
优化周期。简言之,预处理语句使用更少的资源,因而运行得更快。 提供给预处理语句的参数不需要用引号括起来,驱动程序会处理这些。如果应用程序独占地使用预处理语句,那么可以确保没有
SQL
入侵发生。(然而,如果您仍然将查询的其他部分建立在不受信任的输入之上,那么就仍然存在风险)。 预处理语句是如此有用,以致
PDO
实际上打破了在目标
4
中设下的规则:如果驱动程序不支持预处理语句,那么
PDO
将仿真预处理语句。
清单 4.
使用预处理语句的重复插入
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); $stmt->bindParam(':name', $name);$stmt->bindParam(':value', $value); // insert one row$name = 'one';$value = 1;$stmt->execute(); // insert another row with different values$name = 'two';$value = 2; $stmt->execute(); |
清单 5.
使用预处理语句取数据
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?"); if ($stmt->execute(array('one'))) { while ($row = $stmt->fetch()) { print_r($row); }} |
如果数据库驱动程序支持,您还可以绑定输出和输入参数。输出参数通常用于从
存储过程
获取值。输出参数使用起来比输入参数要复杂一些,当绑定一个给定的输出参数时,必须知道该参数的长度。如果为参数绑定的值大于您建议的长度,那么就会产生错误。
清单 6.
带输出参数调用存储过程
$stmt = $dbh->prepare("CALL sp_returns_string(?)"); $stmt->bindParam(1, $return_value, PDO_PARAM_STR, 4000); // call the stored procedure$stmt->execute(); print "procedure returned $return_value\n"; |
清单 7.
带输入
/
输出参数调用存储过程
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)"); $value = 'hello'; $stmt->bindParam(1, $value, PDO_PARAM_STR|PDO_PARAM_INPUT_OUTPUT, 4000); // call the stored procedure$stmt->execute(); print "procedure returned $value\n"; |
4
、错误处理
PDO
提供了
3
种不同的错误处理模式,以满足不同风格的编程:
PDO_ERRMODE_SILENT
这是默认模式。
PDO
将只设置
错误代码
,以通过 errorCode()
和
errorInfo()
方法对语句和
数据库对象
进行检查。如果错误是由于对语句对象的调用而产生的,那么可以在那个对象上调用 errorCode()
或
errorInfo()
方法。如果错误是由于调用数据库对象而产生的,那么可以在那个数据库对象上调用上述两个方法。
PDO_ERRMODE_WARNING
除了设置错误代码以外,
PDO
还将发出一条传统的
E_WARNING
消息。如果您只是想看看发生了什么问题,而无意中断应用程序的流程,那么在调试
/
测试当中这种设置很有用。
PDO_ERRMODE_EXCEPTION
除了设置
错误代码
以外,PDO
还将抛出一个
PDOException
,并设置其属性,以反映错误代码和错误信息。这种设置在调试当中也很有用,因为它会放大脚本中产生错误的地方,从而可以非常快速地指出代码中有问题的潜在区域(记住,如果异常导致脚本终止,则
事务
将自动
回滚
)。 异常模式另一个有用的地方是,与传统的 PHP
风格的警告相比,您可以更清晰地构造自己的错误处理,而且,比起以静寂方式以及显式地检查每个数据库调用的返回值,异常模式需要的代码
/
嵌套也更少。
PDO
定制了使用
SQL-92 SQLSTATE
错误代码
字符串的标准;不同 PDO
驱动程序负责将它们
本地代码
映射为适当的 SQLSTATE
代码。例如,
SQLSTATE
是用于
DB2
(以及通常的
ODBC
)的本地错误代码格式,这是多么方便啊!
errorCode()
方法返回一个
SQLSTATE
代码。如果您需要关于一个错误的更多特定的信息,
PDO
还提供了一个
errorInfo()
方法,该方法将返回一个
数组
,其中包含 SQLSTATE
代码、特定于驱动程序的错误代码以及特定于驱动程序的错误字符串。
分页数据、滚动游标和定位更新
在 Web
应用程序中,一种常见的范例是对查询结果进行
分页
。如果您使用一个 Internet
搜索引擎,那么很可能每天都会做这样的事。您输入
搜索词
,然后得到前 10-20
个匹配项。如果您想看到更多搜索结果,可以单击
"next page"
链接。如果想回头看前面看过的结果,可以单击
"previous page"
链接。记得在几年前,当我第一次在
Web
上使用这样的东西时,我对自己说:
“
为什么我不能通过滚动查看所有数据呢?
”
问题的答案说简单也简单,说复杂也复杂
——
我只想说,
HTTP
不会智能地使数据库上的可滚动
游标
一直处于开放状态,即便如此,需要大量传输的 Web
应用程序也会很快地消耗掉大量开放的可滚动游标。因此,最简单的解决方案是为用户显示所有的匹配项
——
但是用户很容易迷失在大量的结果当中。比较符合逻辑的措施是人工地将数据格式化到多个页面上,使用户可以每次查看一部分可以管理的数据。
所以人们编写可以取所有数据的 PHP
应用程序,然后只显示前
10
行。根据下一次请求,应用程序又显示
11-20
行,依此类推。这对于只返回少量数据的查询来说很不错,但是,如果有很多匹配项(比如多于
100
),那么先取全部数据然后丢弃其中的
90%
,这种做法很浪费。
PHP
的创始人
Rasmus Lerdorf
就这种情形特地为
MySQL
发明了一个特殊的
"LIMIT, OFFSET"
子句。它允许您通知数据库,您只对一小部分行感兴趣,这样它就不会取其他不需要的行了。其语法(或非常类似的东西)已经被其他流行的开放源代码数据库采纳,但并不是所有数据库都提供了相同的语法。
Troels Arvin
收集了一些非常有用的信息,对不同
RDBMS
所支持的语法进行了比较。
如果您想在以 DB2
为
后台数据库
的 PHP
应用程序中实现分页结果,那么可以(也应该)使用下面示例中的语法。这里我们假设有一个
books
表,表中包含书名和作者,我们现在想要每次在一页中显示
10
个以上结果:
清单 8.
使用
SQL Standard "Window Functions"
实现数据分页
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); // the offset is passed in from the user when they click on a link // this cast to integer ensures that no SQL injection can occur $offset = (int)$_GET['offset'];$stmt = $db->prepare("select * from ( select ROW_NUMBER() OVER (ORDER BY author) as rownum, * from books ) as books_windowWHERE rownum > $offset AND rownum <= (10 + $offset)"); if ($stmt->execute()) { while (($row = $stmt->fetch()) !== false) { print_r($row); }} |
Cloudscape 说明在撰写本文之际,Cloudscape 在其 SQL 实现中还不支持 ROW_NUMBER(),所以需要使用可滚动游标。 |
现在,如果您要编写一个更通用的应用程序,并希望实现
分页
的
结果集
,但是不想专门编写很多的代码,并且也不想使用更重量级的抽象层,Troels Arvin
的非常有帮助的
RDBMS
信息建议,您可以使用
游标
作为更轻便(稍微慢一点)的方案。碰巧的是,PDO
具有这方面的
API
级的支持。下面将谈到如何使用这种支持来达到与上面示例相同的效果:
清单 9.
使用滚动游标实现数据分页
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("select * from books order by author", array( PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL)); // the offset is passed in from the user when they click on a link // this cast to integer ensures that no SQL injection can occur $offset = (int)$_GET['offset'];if ($stmt->execute()) { // moves the cursor to the requested offset and fetches the first for ($tofetch = 10, $row = $stmt->fetch(PDO_FETCH_ASSOC, PDO_FETCH_ORI_REL, $offset); $row !== false && $tofetch-- > 0; $row = $stmt->fetch(PDO_FETCH_ASSOC)) { print_r($row); } } |
需要强调的是,虽然滚动
游标
对于更冗长的 window
函数方案来说是一个很方便的替代方案,但这种方案要慢很多。如果在一个传输量比较少的环境中进行测试,您可能发现不了速度上的差异,但当规模扩大时,您就会开始发现速度降慢带来的痛苦。
定位更新
可滚动游标的另一个用途是,基于 SQL
中无法表达的重大标准驱动更新。如果您有一个
Web
页面链接的表,并且需要在每晚的批处理过程中更新那个表,以反映
Web
页面当前大小,那么可以编写如下代码:
清单 10.
使用滚动游标作出定位更新
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); // create a named, scrolling, updateable cursor $stmt = $db->prepare("select url, size from links FOR UPDATE OF size", array( PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL, PDO_ATTR_CURSOR_NAME => 'link_pos'));if ($stmt->execute()) { // a statement for applying our updates. // Notice the WHERE CURRENT OF clause mentions "link_pos", // which is the name of the cursor we're using to select the data $upd = $db->prepare("UPDATE links set size = ? WHERE CURRENT OF link_pos"); // grab each row while (($row = $stmt->fetch()) !== false) { // There are much more efficient ways to do this; // this is a brief example only: grab all the content // from the URL $content = file_get_conents($row['url']); // and measure its length $size = strlen($content) // and pass that as a parameter to our update statement $upd->execute(array($size)); }} |
大型对象
在应用程序中的
某个地方
,您可能发现需要在数据库中存储“
大型(
large
)
”
数据。大型通常意味着
“
大约
4kb
或
4kb
以上
”
,尽管在没有
“
大型
”
数据之前
DB2
最大可以处理
32kb
的数据。 大型对象可以是文本的,也可以是二进制的。
PDO
允许在
bindParam()
或
bindColumn()
调用中通过使用
PDO_PARAM_LOB
类型代码来使用大型数据类型。
PDO_PARAM_LOB
告诉
PDO
将数据映射为流,所以可以使用
PHP Streams API
来操纵这样的数据。下面是一个示例:
5
、程序功能
清单 11.
从数据库取一副图像
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("select contenttype, imagedata from images where id=?"); $stmt->execute(array($_GET['id']));list($type, $lob) = $stmt->fetch(); header("Content-Type: $type");fpassthru($lob); |
上面的介绍很简明扼要。现在让我们试试另一面,将上传的图像插入到一个数据库中:
清单 12.
将图像插入数据库中
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)"); $id = get_new_id(); // some function to allocate a new ID // assume that we are running as part of a file upload form // You can find more information in the PHP documentation $fp = fopen($_FILES['file']['tmp_name'], 'rb');$stmt->bindParam(1, $id); $stmt->bindParam(2, $_FILES['file']['type']); $stmt->bindParam(3, $fp, PDO_PARAM_LOB); $stmt->execute(); |
这两个例子都是宏观层次的。请记住,被取的大型对象是一个流,可以通过所有常规的流函数来使用它,例如 fgets()
、
fread()
、
fgetcsv()
和
stream_get_contents()
。
关于全球化、NLS
和字符集的简要说明
在越来越多的 PHP
应用程序中,越来越重要的一点是让应用程序能够在全球范围内使用。从实践角度来讲,这意味着应用程序需要能够正确地处理多种语言(例如英语和日语)中的数据,并且其功能性不变。这是一个很大的专题,做起来很有技巧性。实现全球化要走的第一步是采用一种适合所有数据的全球编码,例如
UTF-8
。这是一种
ASCII
兼容的编码,它可以使用特殊字符序列为整个
unicode
字符集
编码。UTF-8
也是一种多
字节
编码。
与常规 ASCII
字符
串相比,多字节编码的字符串处理起来要棘手一点,因为一个或多个字符对应于一个给定的字母 ——
例如,
UTF-8
允许最多
6
个字符的序列映射到字符串中的一个字母。
ASCII
字符在
UTF-8
中仍具有相同的表示,因此,如果只是处理不带任何特殊音调的纯英文文本,则
UTF-8
看上去就像是
ASCII
。这意味着类似的
字符串函数
(作用于字节而不是字符位置),例如 strlen()
和
substr()
,可能得不到预期的效果,这取决于
UTF-8
字符串中的内容。幸运的是,
PHP iconv
扩展为这些函数提供了一些编码感知的替代函数。例如,您可以使用
iconv_strlen()
来得出
字符
串中的字符数,而不是使用 strlen()
。类似地,您可以不用
strpos()
或
substr()
,而使用
iconv_strpos()
和
iconv_substr()
。
iconv
扩展为您提供了在处理多种编码下的数据时所需的基本工具。应用程序应该尽量确保所有数据都是
UTF-8
编码的。如果用适当的
Content-Type
标记
Web
页面,那么大多数浏览器将发送
UTF-8
编码的数据,可以确信,一定有一个编码类型属性可应用于
HTML FORM
标签。
接下来的一步是设置 DB2
实例,使它在您与之交互时使用
UTF-8
。这很容易办到,只需在
DB2
的命令行提示符中运行以下命令:
清单 14.
设置
DB2
实例,使之使用
UTF-8
$ db2set DB2CODEPAGE=1208 |
完成这样的更改后,从 DB2
实例取到的所有文本数据都是
UTF-8
编码的。同样,
DB2
期望您输入的所有文本也是
UTF-8
编码的。当应用程序的每个部分都使用
UTF-8
时,应用程序就可以全球使用了,并且能够显示任何语言的文本,只要这种语言的文本可以用
UTF-8
编码。前面我已经暗示过,这只是通往国际化大道的第一步。还有很多其他的事情需要考虑,例如本地化(采用给定用户的地区设置来显示日期、时间、重量和度量,将通用文本翻译成用户本地的语言)、从右到左或双向(
bi-di
)文本布局,等等。
值得注意的是,PDO
不对该数据做任何特殊的事情。有些驱动程序允许更改为一个连接使用的编码,但是在
PDO
级没有处理这种事情的特殊逻辑。其原因是,
PHP
内部完全不知道
unicode
,所以在这里试图使
PDO
知道
unicode
是没有意义的。如果您对这方面的专题感兴趣,那么您会欣喜地得知,
PHP
的
unicode
支持很快就要出现,不过我也不知道它初次露面的确切日期。
丙二醇
丙二醇
简称PDO
中文名称: 1,3-
丙二醇
英文名称: 1,3-propanediol
英文名称2
:
1,3-dihydroxypropane
分子式: C3H8O2
结构: HOCH2CH2CH2OH
CAS No.
:
504-63-2
分子量: 76.10
外观与性状: 无色、无臭,具咸味、吸湿性的粘稠液体。(
纯品
)
熔点(℃)
:
-27
沸点(℃)
:
210-211
相对密度(
水
=1)
:
1.05(25℃)
相对蒸气密度(
空气
=1)
:
2.6
饱和蒸气压(kPa)
:
0.13(60℃)
闪点(℃)
:
79
引燃温度(℃)
:
400
爆炸上限%(V/V)
: 无资料
爆炸下限%(V/V)
: 无资料
溶解性: 与水混溶,可混溶于乙醇、乙醚。
主要用途: 用作溶剂,
用于有机合成。
石油公司
petroleum development of Oman.
阿曼石油开发公司
进程数据对象
工程上,PDO
(
Process data object
)是
CANopen
专有名词。
[1]
进程数据对象
(
PDO
)协定可用来在许多节点之间交换即时的资料。可透过一个PDO
,传送最多
8
字节(
64
位元)资料给一设备,或由一设备接收最多
8
字节(
64
位元)的资料。一个
PDO
可以由对象字典中几个不同索引的资料组成,规划方式则是透过对象字典中对应
PDO mapping
及
PDO
参数的索引。
[1]
PDO
分为两种:传送用的
TPDO
及接收用的
RPDO
。一个节点的
TPDO
是将资料由此节点传输到其他节点,而
RPDO
则是接收由其他节点传输的资料。一个节点分别有
4
个
TPDO
及
4
个
RPDO
。
[1]
PDO
可以用同步或异步的方式传送:同步的
PDO
是由
SYNC
讯息触发,而异步的
PDO
是由节点内部的条件或其他外部条件触发。例如若一个节点规划为允许接受其他节点产生的
TPDO
请求,则可以由其他节点送出一个没有资料但有设定
RTR
位元的
TPDO
(
TPDO
请求),使该节点送出需求的资料。
[1]
借由RPDO
也可以使两个或两个以上的设备同时启动。只要将其
RPDO
对应到相同的
TPDO
即可。
[1]
太平洋十年涛动
Pacific Decade Oscillation
PDO
是指中纬度太平洋盆地地区,海洋与大气气候变化的循环模式。由北纬20°
的太平洋表面的温度所得。
PDO
本词条缺少
信息栏
,补充相关内容使词条更完整,还能快速升级,赶紧来
编辑
吧!
PDO
一是
PHP
数据对象(
PHP Data Object
)的缩写。二是
丙二醇
简称PDO
。三是阿曼石油开发公司 (
petroleum development of Oman.
)的缩写,四是过程数据对象(
Process data object
)的缩写。五是太平洋十年涛动
目录
PHP数据对象
PDO
并不提供数据库抽象,它并不会重写
SQL
或提供数据库本身缺失的功能,如果你需要这种功能,你需要使用一个更加成熟的抽象层。
PDO
需要
PHP5
核心
OO
特性的支持,所以它无法运行于之前的
PHP
版本。
运行
在Unix
环境下
PHP5.1
以上版本中:
如果你正在使用PHP5.1
版本,
PDO
和
PDO SQLITE
已经包含在了此发行版中;当你运行
configure
时它将自动启用。推荐你将
PDO
作为共享扩展构建,这样可以使你获得通过
PECL
升级的好处。推荐的构建支持
PDO
的
PHP
的
configure line
应该也要启用
zlib
。你也应该启用你选择的数据库的
PDO
驱动 ;关于这个的更多信息请查看
database-specific PDO drivers
,但要注意如果你将
PDO
作为一个共享扩展构建,你必须也要将
PDO
驱动构建为共享扩展。
SQLite
扩展依赖于
PDO
,所以如果
PDO
作为共享扩展构建,
SQLite
也应当这样构建
./configure --with-zlib --enable-pdo=shared --with-pdo-sqlite=shared --with-sqlite=shared
将PDO
安装为一个共享模块后,你必须编辑
php.ini
文件使得在
PHP
运行时自动载入
PDO
扩展。你同样需要启用那儿的特定数据库驱动;确保他们列出在
pdo. so
行之后,因为
PDO
必须在特定数据库驱动载入之前初始化。如果你是以静态方式构建的
PDO
和特定数据库驱动扩展,你可以跳过这一步。
extension=pdo. so
让PDO
作为一个共享的模块将使你可以在新版
PDO
发布时运行
pecl upgrade pdo
命令升级,而不用强制你重新构建整个
PHP
。注意如果你是这样做的,你也需要同时升级你的特定数据库驱动。
在windows
环境下
PHP5.1
以上版本中:
PDO
和主要数据库的驱动同
PHP
一起作为扩展发布,要激活它们只需简单的编辑
php.ini
文件:
extension=php_pdo.dll
然后,选择针对特定数据库的DLL
文件使用
dl()
在运行时加载,或者在
php.ini
文件中
php_pdo.dll
行后启用它们,如:
extension=php_pdo.dll
extension=php_pdo_firebird.dll
extension=php_pdo_informix.dll
extension=php_pdo_mssql.dll
extension=php_pdo_mysql.dll
extension=php_pdo_oci.dll
extension=php_pdo_oci8.dll
extension=php_pdo_odbc.dll
extension=php_pdo_pgsql.dll
extension=php_pdo_sqlite.dll
这些DLL
文件应当存在于系统的
extension_dir
目录里。
注意 PDO_INFORMIX
只能作为一个
PECL
扩展使用。
========================================================================================
PHP 5.1
发布时将附带一个全新的数据库连接层,即
PHP Data Objects (PDO)
。虽然
PHP
一直都拥有很好的数据库连接,但
PDO
让
PHP
达到一个新的高度。学习如何获得、安装和使用
PDO
,以连接到
IBM? DB2? Universal Database?
和
IBM Cloudscape?
数据库,插入和检索数据,并探索更多高级特性,例如预处理语句(
prepared statements
)、绑定参数(
bound parameters
)、可滚动游标(
scrollable cursors
)、
定位更新
(positioned updates
)以及
LOB
。
背景
随着拥有更成熟 OO
语法的
PHP 5
的发布,
PHP
越来越多地受到越来越大的机构的关注,对于
PHP
来说,提供更加一致的和可访问的数据访问
API
变得越来越重要。
PHP
与流行的
开放源代码关系数据库管理系统
(RDBMS
)
MySQL
之间总是很有默契。这对拍档的成功很大程度上是由于它们免费可用,而且进入的门槛也比较低,这两种产品的合作使它们各自都取得了广受推崇的地位。
很多 PHP
应用程序开发人员都习惯于
PHP-MySQL
这对组合,以致
PHP
对其他数据库的支持常常模仿
MySQL
客户机库
API
。然而,并不是所有的数据库客户机
API
都是一样的,也不是所有的数据库都提供相同的特性。虽然存在模仿,但不同的
PHP
数据库扩展都有它们各自的怪僻和不同之处,所以从一种数据库迁移到另一种数据库时会有一些困难。虽然这不是创建
PDO
的直接原因,但是在设计过程中还是有一定影响的。
如果您是带着想结合使用 PHP
和
DB2
的目的阅读本文,那么您很可能属于以下类型中的一种:
您从一家小公司开始,在 MySQL
(举个例子)上运行
PHP
,由于业务增长,您需要
DB2
所提供的可伸缩性
/
可靠性
/
支持或其他特性。您希望移植代码,以使用
DB2
,但由于
API
的变化,您需要编写或实现一个抽象层,以便在
DB2
上测试应用程序的同时可以继续在旧的数据库上运行。不仅如此,您还希望能有自己的选择,并保留支持其他
RDBMS
的可能性,因为您清楚,有些客户机可能已经和其他平台栓在一起了。
您用 PHP
在
MySQL
之上构建了一个小型的部门应用程序(同样,这只是举个例子,我并不是要跟
MySQL
过不去)。事实证明这个应用程序本身很有用,现在已经在这个部门之外使用,并且闯入了
CIO/CTO
的法眼
——
现在需要遵从托管的标准数据库。(是的,这是第一点的一个变种。) 在其他某些复杂的企业级应用程序的后台,您已经有一个
DB2
实例,您希望利用
PHP
的快速应用程序开发和
原型设计
来生成动态报告。
目标
至此我们已经掌握了数据库及 PHP
的背景知识,现在正好可以提及
PDO
背后的一些设计目标:
为大多数数据库 API
中的常见特性提供一致的
API
。
具有可扩展性,以使数据库供应商 X
仍然可以暴露特性
Y
并保持
PDO
的兼容性。
提供大量基本的兼容性技巧,以便能够更方便地创建跨数据库兼容的应用程序。
不为给定数据库 API
中本来没有的特性(例如序列)提供完全抽象或仿真。
PDO
类意图为您提供对数据库本地特性的一致性访问,并减少干扰。
通过将与 PHP
内部打交道的代码(这是最难于编写的部分)集中起来,简化
PHP
数据库驱动程序的创建。
最后一点非常重要。PDO
是模块化结构,它被分成一个公共核心以及一个或多个驱动程序扩展,公共核心提供了在脚本(
PDO
本身)中使用的
API
,驱动程序扩展则为
PDO
和本地
RDBMS
客户机
API
库架起一座桥梁。
DB2
用户将会希望使用
PDO_ODBC
驱动程序,据称它可以提供以下特性:
它经过重新编写,能支持遵从 ODBC V3
的驱动程序和驱动程序管理器。它还考虑了对
DB2
特定特性和优化的支持,这成为设计过程中的一部分
——
不是后来补充的。
对于取 10,000
行记录这样的
DB2
访问操作,使用
PDO_ODBC
驱动程序时的性能比使用传统的
PHP Unified ODBC
扩展要快大约
10
倍。之所以有这么大的差异,是因为在
PDO
中默认的
游标
是轻量级的只能向前移动的游标。
获取
PHP 5.1
发布时将附带
PDO
,但是也可以通过
PECL
这个
PHP
扩展库(
PHP Extension Repository
)来结合使用
PDO
和
PHP 5.0.3
及以上版本。如果您使用的是
Windows
,那么您会欣喜地发现安装过程要简单得多。
我将假设您已经拥有配置 PHP 5
使之使用您选择的
Web
服务器的经验,只有在此假设下,我才能集中精力关注更相关的细节。同样,我还将假设您使用的是一个
DB2 Universal Database
服务器或
网络服务器
模式下的 IBM Cloudscape
数据库,并且接受了用户为
db2inst1
、密码为
ibmdb2
的默认安装选项。如果您自己编译驱动程序,那么在进行编译的机器上,应该安装有
DB2
客户机,并且存在应用程序开发
header
,否则编译将遭到失败。
安装
在 PHP 5.0.3
及以上版本上通过
PECL
进行安装
默认情况下,PHP
将安装
"PEAR"
包管理系统。您选择的
OS
发行版很可能已经创建了一个包含
PEAR
的组件的包,很可能您已经安装了这个包,并准备运行它。让我们试验一下。
如果它不能工作如果您没能看到类似于左侧文本的输出,那么很可能您没有安装需要的所有包。这时应查阅您选择的 OS 发行版的文档,看看接下来应该做什么。或者,您可以自己编译 PHP。 |
清单 1.
列出已安装的
PEAR
包
$ pear listInstalled packages:=================== Package Version StateArchive_Tar 1.1 stable Console_Getopt 1.2 stablePEAR 1.3.4 stable XML_RPC 1.1.0 stable |
这个包列表表明,我已经安装了 PEAR 1.3.4
。很可能您也会安装那个版本。为了成功地安装
PDO
,需要升级到
PEAR 1.3.5
;这个过程很快,很顺利:
$ sudo pear upgrade PEAR |
现在便可以放心安装 PDO
了:
$ sudo pear install PDO |
您已经安装了 PDO
核心,为了使之生效,需要在
php.ini
文件中启用它。您需要添加以下一行:
extension=pdo. so |
现在安装用于 PDO
的
ODBC
驱动程序,如果您需要连接到
DB2
、
Cloudscape
或
Apache Derby
,就需要这个驱动程序:
$ sudo pear install PDO_ODBC |
您将看到这样的提示:flavour,dir ? (just leave blank for help)
。这是一个稍微有点隐蔽的提示,它询问需要配置哪种类型的
ODBC
驱动程序,以及将它安装在哪里。如果在安装
DB2
时选择了默认安装选项,那么可以输入
ibm-db2
,这相当于
ibm-db2,/home/db2inst1/sqllib
。如果您选择了不同的安装位置,那么应该用它置替换
/home/db2inst1/sqllib
。输入了正确的细节后,按下
enter
键,这样驱动程序就会构建和安装。
您需要将驱动程序添加到 php.ini
文件中,从而激活驱动程序。确保将下面这一行添加在之前所添加的
pdo. so
这一行
之后
,否则 PHP
不能正确地初始化。
extension=pdo_odbc.so |
PHP 5.1
及以上版本上的安装
PHP 5.1
发布时附带了
PDO
。为获得
DB2
支持,只需将下面的开关添加到配置行。您显然希望添加更多的配置选项,以满足您自己的
Web
服务器。关于这方面的详细内容,可以查看
PHP
文档。我将假设您使用的是一个最近的
Linux?
发行版,并运行
Apache 2:
$ tar xjf php-5.1.0.tar.bz2$ cd php-5.1.0 $ ./configure --with-pdo-odbc=ibm-db2,/home/db2inst1/sqllib \ --with-apxs2=/usr/sbin/apxs$ make $ sudo make install |
Windows
上的安装
Windows
上的安装比起
UNIX?
上的安装来要简单一些。如果您下载了
PHP 5.1
,那么就已经拥有了这个包中相关的
DLL
。否则,您需要从
PHP
快照站点(请参阅本文后面的
“
下载
”
小节)下载这些
DLL
。
为了激活 PDO
,将下面两行添加到
php.ini
文件:
extension=php_pdo.dll extension=php_pdo_odbc.dll |
重新启动 Web
服务器
安装完毕后,应该完全重新启动 Web
服务器,以确保
PHP
装载新的扩展,这样就可以开始使用
PDO
了。如果您使用的是
UNIX
平台,那么需要获得
DB2
客户机的
DB2
实例环境,以便正确地初始化。如果您使用的是
bourne shell
型的
shell
,那么可以通过
运行命令
. /home/db2inst1/sqllib/db2profile
来获得。(注意
:
假定开头部分的句号已经在那里!)您需要作出安排,使之在
Web
服务器启动脚本中自动发生。
关键概念
1
、连接管理
连接是通过创建 PDO
基类的实例而建立的。不管您想要使用哪种驱动程序,您总是使用
PDO
类名。
构造函数
接受用于指定
数据源
(即 DSN
)的参数,可能还包括用户名和密码参数(如果有的话)。最后一个参数用于传递附加的调优参数到
PDO
或底层驱动程序
——
后面很快会有更详细的论述。下面是一个简短的连接到
DB2
的示例脚本:
清单 2.
如何使用
PDO
连接到
DB2
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); echo "Connected\n";} catch (Exception $e) { echo "Failed: " . $e->getMessage();} |
odbc:SAMPLE
告诉
PDO
它应该使用
ODBC
驱动程序,并且应该使用
"SAMPLE"
数据库。如果使用一个驱动程序管理器,那么可以用一个
ODBC
级
数据源
名称替代 SAMPLE
。实际上,在冒号
字符
之后可以指定任何有效的 ODBC
数据源连接字符串。
如果连接成功,您将看到消息 "Connected"
,否则,
PDO
将抛出一个
PDOException
,解释为什么连接失败。可能的原因包括无效的参数,不正确的用户
/
密码,甚至是您忘了装载驱动程序。
值得注意的是,除非您捕捉从
构造函数
抛出的异常,否则,如果 PHP
脚本未能连接到数据库,它将终止。这与传统的
PHP
数据库扩展有很大的不同。对于不喜欢异常的人来说,只有两个
“
硬故障(
hard-failure
)
”
点可能
抛出异常
,这是其中一个点(另一个地点是,当您试图使用
事务
时缺乏对事务的支持)。对于所有其他错误,PDO
将使用您选择的 错误处理设置。
连接将保持开放状态,直到所有对它的引用被释放。如果在主脚本的顶端打开连接,并将其句柄存储在一个
全局变量
中,那么该连接将一直处于开放状态,直到脚本结束,或者直到 $dbh
变量被设为
null
。如果在一个函数中打开连接,并且只将句柄存储在一个本地变量中,那么当函数返回时,连接将被关闭。这些语义对于
PHP
中的任何对象都是一样的,没有什么特别的地方。
ODBC 连接池如果您使用的是 Windows,或者如果您选择在 UNIX 型平台上使用一个 ODBC 驱动程序管理器,那么值得注意的是,PDO_ODBC 将自动尝试使用该驱动程序管理器的 ODBC 连接池特性。这个特性类似于 PHP 级连接缓存,不要求专门请求一个持久的连接。此外,缓存是在 ODBC 级进行的,这意味着在同一个进程中运行的其他组件(例如在 IIS 下运行的 ASP/.Net 脚本)也能利用相同的连接池。 |
对于流量较大的站点,让 PHP
在不同请求的间隙中缓存打开的连接,使得每个进程(每个惟一的连接参数集)只需花费一次建立连接的成本,这样做常常很有益处。虽然这听起来像是一个不错的想法,但您应该仔细评估这样做对系统的影响,因为当大量缓存的连接空闲在那里的时候,就会适得其反。
要建立一个缓存的连接(如果您更熟悉传统的数据库扩展的话,也可以说是 *pconnect()
),需要在实例化数据库连接时传递一个属性:
清单 3.
如何用
PDO
连接到
DB2
,使用持久(缓存)连接
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO_ATTR_PERSISTENT => true)); echo "Connected\n"; } catch (Exception $e) { echo "Failed: " . $e->getMessage(); } |
2
、事务提交
至此,您已经通过 PDO
连接到了
DB2
,在发出查询之前,您应该理解
PDO
是如何管理
事务
的。如果之前没有接触过事务,那么首先要知道事务的 4
个特征:原子性(
Atomicity
)、一致性(
Consistency
)、独立性(
Isolation
)和持久性(
Durability
),即
ACID
。用外行人的话说,对于在一个事务中执行的任何工作,即使它是分阶段执行的,也一定可以保证该工作会安全地应用于数据库,并且在工作被提交时,不会受到来自其他连接的影响。事务性工作可以根据请求自动撤销(假设您还没有提交它),这使得脚本中的错误处理变得更加容易。
警告只有在通过 PDO::beginTransaction() 启动事务的情况下,才会发生自动回滚。如果手动地发出开始一个事务的查询,那么 PDO 就无法知道该事务,从而不能在必要时进行回滚。 |
不幸的是,并不是每种数据库都支持事务,所以当第一次打开连接时,PDO
需要在所谓的
“
自动提交(
auto-commit
)
”
模式下运行。自动提交模式意味着,如果数据库支持
事务
,那么您所运行的每一个查询都有它自己的隐式事务,如果数据库不支持事务,每个查询就没有这样的事务。如果您需要一个事务,那么必须使用 PDO::beginTransaction()
方法来启动一个事务。如果底层驱动程序不支持事务,那么将会抛出一个
PDOException
(无论错误处理设置是怎样的:这总是一个严重错误状态)。在一个事务中,可以使用
PDO::commit()
或
PDO::rollBack()
来结束该事务,这取决于事务中运行的代码是否成功。
DB2 特性虽然我认为事务通常要更快一些,但您还是应该自己评估事务是否真的可以加快代码。例如,在高并发环境中您可能会发现,过度使用事务会增加锁开销。如果在应用程序中出现这种情况,那么建议的补救办法是在一般情况下使用自动提交,而对于真正需要全部 ACID 特征的代码部分则仍然使用事务。 |
当脚本结束时,或者当一个连接即将被关闭时,如果有一个未完成的事务,那么 PDO
将自动
回滚
该事务。这是一种安全措施,有助于避免在脚本非正常结束时出现不一致的情况 ——
如果没有显式地提交事务,那么假设有
某个地方
会出现不一致,所以要执行回滚,以保证数据的安全性。
清单 4.
在事务中执行批处理
try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO_ATTR_PERSISTENT => true)); echo "Connected\n"; $dbh->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION); $dbh->beginTransaction(); $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')"); $dbh->exec("insert into salarychange (id, amount, changedate) values (23, 50000, NOW())"); $dbh->commit(); } catch (Exception $e) { $dbh->rollBack(); echo "Failed: " . $e->getMessage(); } |
在上面的示例中,假设我们为一个新雇员创建一组条目,这个雇员有一个 ID
号,即
23
。除了输入这个人的基本数据外,我们还需要记录雇员的薪水。两个更新分别完成起来很简单,但通过将这两个更新包括在
beginTransaction()
和
commit()
调用中,就可以保证在更改完成之前,其他人无法看到更改。如果发生了错误,
catch
块可以
回滚事务
开始以来发生的所有更改,并打印出一条错误消息。
并不是一定要在事务中作出更新。您也可以发出复杂的查询来提取数据,还可以使用那种信息构建更多的更新和查询。当事务在活动时,可以保证其他人在工作进行当中无法作出更改。事实上,这不是 100%
的正确,但如果您之前没有听说过事务的话,这样介绍也未尝不可。
关于 PHP
应用程序中安全性的说明
PHP Security Consortium 虽然本文表明在使用 PDO 时不再需要引用输入,但这不是说您应该盲目地使数据通过数据库。XSS 攻击是很实际的危险。您应该总是确保对传入应用程序的不受信任的数据应用适当的过滤器,并采取措施避免让不受信任的数据在站点上发出 HTML 或 javascript。 请访问 The PHP Security Consortium 以了解关于这些危险的更多知识,以及应该如何避免这些危险。 |
很多 PHP
脚本中一个常见的缺陷是缺乏输入检验。这种缺陷可以被利用,从而招致
XSS
(
Cross Site Scripting
)以及
SQL
入侵攻击。在
SQL
入侵中,不受信任的数据(例如发给
Web
网页的反馈)和其他文本被衔接在一起,构成一个查询。攻击者可以蓄意地安排他们的输入,使之溢出引号之外,并在您想运行的真正查询后面链接上任意一个查询。这种攻击使攻击者可以更新、插入或删除数据,甚至可能可以看到数据库中的任意信息。
XSS
也是一个类似的问题。不过这一次不受信任的数据瞄准的是浏览站点的人们,而不是应用程序本身。通过提交包含
HTML
或
javascript
组合的文本,攻击者期望您之后会将那种数据直接输出到其他访问站点的人那里,从而使
恶意代码
可以在站点访问者的浏览器上运行。
在编写应用程序时,需要同时考虑这两种攻击。如果小心地检验和过滤输入,这两种攻击都是可以防止的。对 XSS
的处理很有技巧性,所以在这里我不便多讲(不过可以从侧栏找到有用的参考资料)。相比之下,
SQL
入侵更容易对付。您只需在构造查询之前,适当地排除每块不受信任的数据。这种事情有点烦杂,特别是当您有大量的字段要处理时,很容易忘记做这件事。
虽然这是有用的(并且也是重要的)信息,但是您可能想知道,为什么我要花时间提到这一点,本文的重点不是结合使用 PDO
和
DB2
吗?原因是这样的:
PHP
现在得到很广泛的部署,自然地,大量流行的基于
PHP
的应用程序也得到了广泛的部署。每当某一种这样的应用程序(和
PHP
本身没有联系)被发现存在漏洞时,
PHP
常常被误认为是不安全的,可被利用的或者有缺陷的。为了避免将来出现这样的情况,我们可以采取的一个措施是鼓励应用程序开发人员多考虑安全问题,从而减少由诚实的错误导致的损害。扯远了,下面继续介绍其他关键概念。
3
、预处理过程
很多更成熟的数据库都支持预处理语句的概念。什么是预处理语句?您可以把预处理语句看作您想要运行的 SQL
的一种编译过的模板,它可以使用变量参数进行定制。预处理语句可以带来两大好处:
查询只需解析(或准备)一次,但是可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查询,这个过程要花比较长的时间,如果您需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/
编译
/
优化周期。简言之,预处理语句使用更少的资源,因而运行得更快。 提供给预处理语句的参数不需要用引号括起来,驱动程序会处理这些。如果应用程序独占地使用预处理语句,那么可以确保没有
SQL
入侵发生。(然而,如果您仍然将查询的其他部分建立在不受信任的输入之上,那么就仍然存在风险)。 预处理语句是如此有用,以致
PDO
实际上打破了在目标
4
中设下的规则:如果驱动程序不支持预处理语句,那么
PDO
将仿真预处理语句。
清单 4.
使用预处理语句的重复插入
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); $stmt->bindParam(':name', $name);$stmt->bindParam(':value', $value); // insert one row$name = 'one';$value = 1;$stmt->execute(); // insert another row with different values$name = 'two';$value = 2; $stmt->execute(); |
清单 5.
使用预处理语句取数据
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?"); if ($stmt->execute(array('one'))) { while ($row = $stmt->fetch()) { print_r($row); }} |
如果数据库驱动程序支持,您还可以绑定输出和输入参数。输出参数通常用于从
存储过程
获取值。输出参数使用起来比输入参数要复杂一些,当绑定一个给定的输出参数时,必须知道该参数的长度。如果为参数绑定的值大于您建议的长度,那么就会产生错误。
清单 6.
带输出参数调用存储过程
$stmt = $dbh->prepare("CALL sp_returns_string(?)"); $stmt->bindParam(1, $return_value, PDO_PARAM_STR, 4000); // call the stored procedure$stmt->execute(); print "procedure returned $return_value\n"; |
清单 7.
带输入
/
输出参数调用存储过程
$stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)"); $value = 'hello'; $stmt->bindParam(1, $value, PDO_PARAM_STR|PDO_PARAM_INPUT_OUTPUT, 4000); // call the stored procedure$stmt->execute(); print "procedure returned $value\n"; |
4
、错误处理
PDO
提供了
3
种不同的错误处理模式,以满足不同风格的编程:
PDO_ERRMODE_SILENT
这是默认模式。
PDO
将只设置
错误代码
,以通过 errorCode()
和
errorInfo()
方法对语句和
数据库对象
进行检查。如果错误是由于对语句对象的调用而产生的,那么可以在那个对象上调用 errorCode()
或
errorInfo()
方法。如果错误是由于调用数据库对象而产生的,那么可以在那个数据库对象上调用上述两个方法。
PDO_ERRMODE_WARNING
除了设置错误代码以外,
PDO
还将发出一条传统的
E_WARNING
消息。如果您只是想看看发生了什么问题,而无意中断应用程序的流程,那么在调试
/
测试当中这种设置很有用。
PDO_ERRMODE_EXCEPTION
除了设置
错误代码
以外,PDO
还将抛出一个
PDOException
,并设置其属性,以反映错误代码和错误信息。这种设置在调试当中也很有用,因为它会放大脚本中产生错误的地方,从而可以非常快速地指出代码中有问题的潜在区域(记住,如果异常导致脚本终止,则
事务
将自动
回滚
)。 异常模式另一个有用的地方是,与传统的 PHP
风格的警告相比,您可以更清晰地构造自己的错误处理,而且,比起以静寂方式以及显式地检查每个数据库调用的返回值,异常模式需要的代码
/
嵌套也更少。
PDO
定制了使用
SQL-92 SQLSTATE
错误代码
字符串的标准;不同 PDO
驱动程序负责将它们
本地代码
映射为适当的 SQLSTATE
代码。例如,
SQLSTATE
是用于
DB2
(以及通常的
ODBC
)的本地错误代码格式,这是多么方便啊!
errorCode()
方法返回一个
SQLSTATE
代码。如果您需要关于一个错误的更多特定的信息,
PDO
还提供了一个
errorInfo()
方法,该方法将返回一个
数组
,其中包含 SQLSTATE
代码、特定于驱动程序的错误代码以及特定于驱动程序的错误字符串。
分页数据、滚动游标和定位更新
在 Web
应用程序中,一种常见的范例是对查询结果进行
分页
。如果您使用一个 Internet
搜索引擎,那么很可能每天都会做这样的事。您输入
搜索词
,然后得到前 10-20
个匹配项。如果您想看到更多搜索结果,可以单击
"next page"
链接。如果想回头看前面看过的结果,可以单击
"previous page"
链接。记得在几年前,当我第一次在
Web
上使用这样的东西时,我对自己说:
“
为什么我不能通过滚动查看所有数据呢?
”
问题的答案说简单也简单,说复杂也复杂
——
我只想说,
HTTP
不会智能地使数据库上的可滚动
游标
一直处于开放状态,即便如此,需要大量传输的 Web
应用程序也会很快地消耗掉大量开放的可滚动游标。因此,最简单的解决方案是为用户显示所有的匹配项
——
但是用户很容易迷失在大量的结果当中。比较符合逻辑的措施是人工地将数据格式化到多个页面上,使用户可以每次查看一部分可以管理的数据。
所以人们编写可以取所有数据的 PHP
应用程序,然后只显示前
10
行。根据下一次请求,应用程序又显示
11-20
行,依此类推。这对于只返回少量数据的查询来说很不错,但是,如果有很多匹配项(比如多于
100
),那么先取全部数据然后丢弃其中的
90%
,这种做法很浪费。
PHP
的创始人
Rasmus Lerdorf
就这种情形特地为
MySQL
发明了一个特殊的
"LIMIT, OFFSET"
子句。它允许您通知数据库,您只对一小部分行感兴趣,这样它就不会取其他不需要的行了。其语法(或非常类似的东西)已经被其他流行的开放源代码数据库采纳,但并不是所有数据库都提供了相同的语法。
Troels Arvin
收集了一些非常有用的信息,对不同
RDBMS
所支持的语法进行了比较。
如果您想在以 DB2
为
后台数据库
的 PHP
应用程序中实现分页结果,那么可以(也应该)使用下面示例中的语法。这里我们假设有一个
books
表,表中包含书名和作者,我们现在想要每次在一页中显示
10
个以上结果:
清单 8.
使用
SQL Standard "Window Functions"
实现数据分页
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); // the offset is passed in from the user when they click on a link // this cast to integer ensures that no SQL injection can occur $offset = (int)$_GET['offset'];$stmt = $db->prepare("select * from ( select ROW_NUMBER() OVER (ORDER BY author) as rownum, * from books ) as books_windowWHERE rownum > $offset AND rownum <= (10 + $offset)"); if ($stmt->execute()) { while (($row = $stmt->fetch()) !== false) { print_r($row); }} |
Cloudscape 说明在撰写本文之际,Cloudscape 在其 SQL 实现中还不支持 ROW_NUMBER(),所以需要使用可滚动游标。 |
现在,如果您要编写一个更通用的应用程序,并希望实现
分页
的
结果集
,但是不想专门编写很多的代码,并且也不想使用更重量级的抽象层,Troels Arvin
的非常有帮助的
RDBMS
信息建议,您可以使用
游标
作为更轻便(稍微慢一点)的方案。碰巧的是,PDO
具有这方面的
API
级的支持。下面将谈到如何使用这种支持来达到与上面示例相同的效果:
清单 9.
使用滚动游标实现数据分页
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("select * from books order by author", array( PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL)); // the offset is passed in from the user when they click on a link // this cast to integer ensures that no SQL injection can occur $offset = (int)$_GET['offset'];if ($stmt->execute()) { // moves the cursor to the requested offset and fetches the first for ($tofetch = 10, $row = $stmt->fetch(PDO_FETCH_ASSOC, PDO_FETCH_ORI_REL, $offset); $row !== false && $tofetch-- > 0; $row = $stmt->fetch(PDO_FETCH_ASSOC)) { print_r($row); } } |
需要强调的是,虽然滚动
游标
对于更冗长的 window
函数方案来说是一个很方便的替代方案,但这种方案要慢很多。如果在一个传输量比较少的环境中进行测试,您可能发现不了速度上的差异,但当规模扩大时,您就会开始发现速度降慢带来的痛苦。
定位更新
可滚动游标的另一个用途是,基于 SQL
中无法表达的重大标准驱动更新。如果您有一个
Web
页面链接的表,并且需要在每晚的批处理过程中更新那个表,以反映
Web
页面当前大小,那么可以编写如下代码:
清单 10.
使用滚动游标作出定位更新
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); // create a named, scrolling, updateable cursor $stmt = $db->prepare("select url, size from links FOR UPDATE OF size", array( PDO_ATTR_CURSOR => PDO_CURSOR_SCROLL, PDO_ATTR_CURSOR_NAME => 'link_pos'));if ($stmt->execute()) { // a statement for applying our updates. // Notice the WHERE CURRENT OF clause mentions "link_pos", // which is the name of the cursor we're using to select the data $upd = $db->prepare("UPDATE links set size = ? WHERE CURRENT OF link_pos"); // grab each row while (($row = $stmt->fetch()) !== false) { // There are much more efficient ways to do this; // this is a brief example only: grab all the content // from the URL $content = file_get_conents($row['url']); // and measure its length $size = strlen($content) // and pass that as a parameter to our update statement $upd->execute(array($size)); }} |
大型对象
在应用程序中的
某个地方
,您可能发现需要在数据库中存储“
大型(
large
)
”
数据。大型通常意味着
“
大约
4kb
或
4kb
以上
”
,尽管在没有
“
大型
”
数据之前
DB2
最大可以处理
32kb
的数据。 大型对象可以是文本的,也可以是二进制的。
PDO
允许在
bindParam()
或
bindColumn()
调用中通过使用
PDO_PARAM_LOB
类型代码来使用大型数据类型。
PDO_PARAM_LOB
告诉
PDO
将数据映射为流,所以可以使用
PHP Streams API
来操纵这样的数据。下面是一个示例:
5
、程序功能
清单 11.
从数据库取一副图像
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("select contenttype, imagedata from images where id=?"); $stmt->execute(array($_GET['id']));list($type, $lob) = $stmt->fetch(); header("Content-Type: $type");fpassthru($lob); |
上面的介绍很简明扼要。现在让我们试试另一面,将上传的图像插入到一个数据库中:
清单 12.
将图像插入数据库中
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2'); $stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)"); $id = get_new_id(); // some function to allocate a new ID // assume that we are running as part of a file upload form // You can find more information in the PHP documentation $fp = fopen($_FILES['file']['tmp_name'], 'rb');$stmt->bindParam(1, $id); $stmt->bindParam(2, $_FILES['file']['type']); $stmt->bindParam(3, $fp, PDO_PARAM_LOB); $stmt->execute(); |
这两个例子都是宏观层次的。请记住,被取的大型对象是一个流,可以通过所有常规的流函数来使用它,例如 fgets()
、
fread()
、
fgetcsv()
和
stream_get_contents()
。
关于全球化、NLS
和字符集的简要说明
在越来越多的 PHP
应用程序中,越来越重要的一点是让应用程序能够在全球范围内使用。从实践角度来讲,这意味着应用程序需要能够正确地处理多种语言(例如英语和日语)中的数据,并且其功能性不变。这是一个很大的专题,做起来很有技巧性。实现全球化要走的第一步是采用一种适合所有数据的全球编码,例如
UTF-8
。这是一种
ASCII
兼容的编码,它可以使用特殊字符序列为整个
unicode
字符集
编码。UTF-8
也是一种多
字节
编码。
与常规 ASCII
字符
串相比,多字节编码的字符串处理起来要棘手一点,因为一个或多个字符对应于一个给定的字母 ——
例如,
UTF-8
允许最多
6
个字符的序列映射到字符串中的一个字母。
ASCII
字符在
UTF-8
中仍具有相同的表示,因此,如果只是处理不带任何特殊音调的纯英文文本,则
UTF-8
看上去就像是
ASCII
。这意味着类似的
字符串函数
(作用于字节而不是字符位置),例如 strlen()
和
substr()
,可能得不到预期的效果,这取决于
UTF-8
字符串中的内容。幸运的是,
PHP iconv
扩展为这些函数提供了一些编码感知的替代函数。例如,您可以使用
iconv_strlen()
来得出
字符
串中的字符数,而不是使用 strlen()
。类似地,您可以不用
strpos()
或
substr()
,而使用
iconv_strpos()
和
iconv_substr()
。
iconv
扩展为您提供了在处理多种编码下的数据时所需的基本工具。应用程序应该尽量确保所有数据都是
UTF-8
编码的。如果用适当的
Content-Type
标记
Web
页面,那么大多数浏览器将发送
UTF-8
编码的数据,可以确信,一定有一个编码类型属性可应用于
HTML FORM
标签。
接下来的一步是设置 DB2
实例,使它在您与之交互时使用
UTF-8
。这很容易办到,只需在
DB2
的命令行提示符中运行以下命令:
清单 14.
设置
DB2
实例,使之使用
UTF-8
$ db2set DB2CODEPAGE=1208 |
完成这样的更改后,从 DB2
实例取到的所有文本数据都是
UTF-8
编码的。同样,
DB2
期望您输入的所有文本也是
UTF-8
编码的。当应用程序的每个部分都使用
UTF-8
时,应用程序就可以全球使用了,并且能够显示任何语言的文本,只要这种语言的文本可以用
UTF-8
编码。前面我已经暗示过,这只是通往国际化大道的第一步。还有很多其他的事情需要考虑,例如本地化(采用给定用户的地区设置来显示日期、时间、重量和度量,将通用文本翻译成用户本地的语言)、从右到左或双向(
bi-di
)文本布局,等等。
值得注意的是,PDO
不对该数据做任何特殊的事情。有些驱动程序允许更改为一个连接使用的编码,但是在
PDO
级没有处理这种事情的特殊逻辑。其原因是,
PHP
内部完全不知道
unicode
,所以在这里试图使
PDO
知道
unicode
是没有意义的。如果您对这方面的专题感兴趣,那么您会欣喜地得知,
PHP
的
unicode
支持很快就要出现,不过我也不知道它初次露面的确切日期。
丙二醇
丙二醇
简称PDO
中文名称: 1,3-
丙二醇
英文名称: 1,3-propanediol
英文名称2
:
1,3-dihydroxypropane
分子式: C3H8O2
结构: HOCH2CH2CH2OH
CAS No.
:
504-63-2
分子量: 76.10
外观与性状: 无色、无臭,具咸味、吸湿性的粘稠液体。(
纯品
)
熔点(℃)
:
-27
沸点(℃)
:
210-211
相对密度(
水
=1)
:
1.05(25℃)
相对蒸气密度(
空气
=1)
:
2.6
饱和蒸气压(kPa)
:
0.13(60℃)
闪点(℃)
:
79
引燃温度(℃)
:
400
爆炸上限%(V/V)
: 无资料
爆炸下限%(V/V)
: 无资料
溶解性: 与水混溶,可混溶于乙醇、乙醚。
主要用途: 用作溶剂,
用于有机合成。
石油公司
petroleum development of Oman.
阿曼石油开发公司
进程数据对象
工程上,PDO
(
Process data object
)是
CANopen
专有名词。
[1]
进程数据对象
(
PDO
)协定可用来在许多节点之间交换即时的资料。可透过一个PDO
,传送最多
8
字节(
64
位元)资料给一设备,或由一设备接收最多
8
字节(
64
位元)的资料。一个
PDO
可以由对象字典中几个不同索引的资料组成,规划方式则是透过对象字典中对应
PDO mapping
及
PDO
参数的索引。
[1]
PDO
分为两种:传送用的
TPDO
及接收用的
RPDO
。一个节点的
TPDO
是将资料由此节点传输到其他节点,而
RPDO
则是接收由其他节点传输的资料。一个节点分别有
4
个
TPDO
及
4
个
RPDO
。
[1]
PDO
可以用同步或异步的方式传送:同步的
PDO
是由
SYNC
讯息触发,而异步的
PDO
是由节点内部的条件或其他外部条件触发。例如若一个节点规划为允许接受其他节点产生的
TPDO
请求,则可以由其他节点送出一个没有资料但有设定
RTR
位元的
TPDO
(
TPDO
请求),使该节点送出需求的资料。
[1]
借由RPDO
也可以使两个或两个以上的设备同时启动。只要将其
RPDO
对应到相同的
TPDO
即可。
[1]
太平洋十年涛动
Pacific Decade Oscillation
PDO
是指中纬度太平洋盆地地区,海洋与大气气候变化的循环模式。由北纬20°
的太平洋表面的温度所得。