范例 1: Select 指令
任务:连结到 Access 的 Northwind DSN,然後在每一列显示头2个栏位。(Northwind 北风资料库,在ODBC设定的DSN,是Access的标准范例资料库)
在这个范例中,我们建立一个 ADOConnection 物件,它代表了和资料库的连结。连结是以 PConnect 函数来初始化的,然後会持续的连结着。任何时候我们要查询资料库时,我们就呼叫 ADOConnection.Execute() 函数,这将会回传一个 ADORecordSet物件。事实上它只是一个指向在fields[]阵列中,目前记录的指标,我们使用MoveNext()来在记录间移动。
注意:另一个很有用的函数 SelectLimit 并没有在这个范例里使用,这个函数允许我们去限制显示的资料笔数。
<? include('ADOdb.inc.php'); # 载入ADODB $conn = &ADONewConnection('access'); # 建立一个连结 $conn->PConnect('northwind'); # 连结到 MS-Access 北风资料库 $recordSet = &$conn->Execute('select * from products'); if (!$recordSet) print $conn->ErrorMsg(); else while (!$recordSet->EOF) { print $recordSet->fields[0].' '.$recordSet->fields[1].'<BR>'; $recordSet->MoveNext(); } $recordSet->Close(); # 选择性执行 $conn->Close(); # 选择性执行 ?>
在这个例子中,$recordSet回传了存在$recordSet->fields阵列里,目前所指向的记录。以栏位编号为索引,起始值为0。我们使用MoveNext()函数来移动到下一笔记录,当到了最後一笔时,EOF属性会被设定为true。当Execute()函数执行有错误时,会回传一个false值,而不是一个recordset物件。
$recordSet->fields[]阵列是由PHP资料库扩充函数库所产生的。有一些资料库扩充函数库仅支援以编号来进行索引,而不支援以栏位名为索引。要强迫使用栏位名索引,也就是要使用关连式阵列,请使用 $ ADODB_FETCH_MODE 全域变数来设定。当一个资料集被Execute()或是SelectLimit()函数建立时,都会储存而且使用储如此类的设定模式。
$ADODB_FETCH_MODE = ADODB_FETCH_NUM; $rs1 = $db->Execute('select * from table'); $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; $rs2 = $db->Execute('select * from table'); print_r($rs1->fields); # shows array([0]=>'v0',[1] =>'v1') print_r($rs2->fields); # shows array(['col1']=>'v0',['col2'] =>'v1')
上面的范例说明,如果要以顺序来存取栏位,就将 $ADODB_FETCH_MODE 的值设为 ADODB_FETCH_NUM,要以关连式阵列(以栏位名)存取栏位,就要将值设为 ADODB_FETCH_ASSOC。
要取得在被选到的记录笔数,你可以使用$recordSet->RecordCount()方法。注意,如果不能确定得到的记录笔数,会回传 -1 。
范例 2: 进阶的 Select 指令(使用 Field 物件)
任务:选取一个资料表,显示最前面的二栏。如果第二栏是一个日期或时间型态栏位,将它格式化成US格式。
<? include('ADOdb.inc.php'); $conn = &ADONewConnection('access'); $conn->PConnect('northwind'); $recordSet = &$conn->Execute('select CustomerID,OrderDate from Orders'); if (!$recordSet) print $conn->ErrorMsg(); else while (!$recordSet->EOF) { $fld = $recordSet->FetchField(1); $type = $recordSet->MetaType($fld->type); if ( $type == 'D' || $type == 'T') print $recordSet->fields[0].' '. $recordSet->UserDate($recordSet->fields[1],'m/d/Y').'<BR>'; else print $recordSet->fields[0].' '.$recordSet->fields[1].'<BR>'; $recordSet->MoveNext(); } $recordSet->Close(); # optional $conn->Close(); # optional ?>
在这个例子中,我们使用 FetchField() 函数来检查第二个栏位的资料型别。这将会回传一个至少有三个栏位的物件,栏位说明如下:
-
name: 栏位名
-
type: 栏位的资料原生型别native field type of column
-
max_length: 栏位的最大长度,部份资料库像MySQL,并不回传栏位的正确值,以这个例子而言,就会回传 -1 。
然後我们使用 MetaType() 去转换原生型别成通用型别,目前通用型别定义如下:
-
C: character 栏位,应该使用 <input type="text"> 标记来取值。
-
X: 文字栏位(Text) , 长文字栏位,使用 <textarea> 标记来显示资料。
-
B: Blob 栏位或者大型的二位元物件(像程式,图档等)。
-
D: 日期栏位
-
T: 时间栏位
-
L: 逻辑栏位(真假值)或位元栏位
-
N: 数字栏位,包含自动进位、编号、整数、浮点数、实数等。
-
R: 序列栏位,包含了序列、自动增进整数,只对被选择的资料库作用。
如果对应型别是日期或时间,那你可以使用 UserDate() 函数来设定输出的日期格式。这个函数会转换 PHP SQL 日期字串格式为使用者定义的格式。 另一个使用MetaType()的时机是在进行SQL新增或更新指令时,资料格式验证用。
范例 3: 新增
新增一笔记录到订单资料表,里面包含了日期和字串,为了能被资料库正常存取,字串必需校正,以避免部份标记字元。例如:有单引号的字串,John's。
<? include('ADOdb.inc.php'); # load code common to ADOdb $conn = &ADONewConnection('access'); # create a connection $conn->PConnect('northwind'); # connect to MS-Access, northwind dsn $shipto = $conn->qstr("John's Old Shoppe",get_magic_quotes_gpc()); $sql = "insert into orders (customerID,EmployeeID,OrderDate,ShipName) "; $sql .= "values ('ANATR',2,".$conn->DBDate(time()).",$shipto)"; if ($conn->Execute($sql) === false) { print 'error inserting: '.$conn->ErrorMsg().'<BR>'; } ?>
在这个范例中,我们看见了ADODB更进一步的日期及标点符号的处理方式。Unix 日期时间标示(长整数)被DBDate()格式化成Access可以接受的格式,而带了缩写符号的 John's Old Shoppe 则被 qstr() 函数处理成 John''s Old Shoppe 字串,以被资料库合法存取。
观察 Execute 指令的错误处理。如果 Execute() 执行有错误发生时,会传回 False 值。而最後的错误讯息可以由 ErrorMsg() 来显示。
附记:php_track_errors旗标可以被启动,以便将错误讯息储存起来。
范例 4: 除错
<? include('ADOdb.inc.php'); # load code common to ADOdb $conn = &ADONewConnection('access'); # create a connection $conn->PConnect('northwind'); # connect to MS-Access, northwind dsn $shipto = $conn->qstr("John's Old Shoppe"); $sql = "insert into orders (customerID,EmployeeID,OrderDate,ShipName) "; $sql .= "values ('ANATR',2,".$conn->FormatDate(time()).",$shipto)"; $conn->debug = true; if ($conn->Execute($sql) === false) print 'error inserting'; ?>
在上面的例子中,我们藉由设定 debug=true 来启动除错模式。这将会在执行指令时会先将SQL指令显示,并且会显示所有的错误讯息,而不需要去呼叫 ErrorMsg() 。显示资料集的部份,可以叁考 rs2html() 范例。
其它的请叁考自定错误处理的说明。
范例 5: MySQL及选单
连结到MySQL资料库 agora ,并且从SQL命令中建立一个 <select> 选单,<option>的标题是第一个栏位,回传值是第二个栏位。
<? include('ADOdb.inc.php'); # load code common to ADOdb $conn = &ADONewConnection('mysql'); # create a connection $conn->PConnect('localhost','userid','','agora');# connect to MySQL, agora db $sql = 'select CustomerName, CustomerID from customers'; $rs = $conn->Execute($sql); print $rs->GetMenu('GetCust','Mary Rosli'); ?>
Here we define a menu named GetCust, with the menu option 'Mary Rosli' selected. See GetMenu(). We also have functions that return the recordset as an array: GetArray(), and as an associative array with the key being the first column: GetAssoc().
这里,我们定义了一个名为GetCust的选单,预设值是'Mary Rosli'。相关说明请叁考 GetMenu() 。我们也将资料集以阵列回传的方式写在 GetArray()方法里。而另外回传关联式阵列的方法则使用 GetAssoc() ,其中第一个栏位是这个栏位的键值。
在 1.50 版以後的 ADODB 里,是使用公共变数 $ADODB_FETCH_MODE 来设定回传的阵列是以编号或是关连式字串做索引。
范例 6: 一次连结两个资料库
<? include('ADOdb.inc.php'); # 载入 ADOdb $conn1 = &ADONewConnection('mysql'); # 建立一个 mysql 连结 $conn2 = &ADONewConnection('oracle'); # 建立一个 oracle 连结 $conn1->PConnect($server, $userid, $password, $database); $conn2->PConnect(false, $ora_userid, $ora_pwd, $tnsname); $conn1->Execute('insert ...'); $conn2->Execute('update ...'); ?>
范例 7: 产生 Update 及 Insert 的SQL指令
ADODB 1.31版起,新增了两个资料集函数:GetUpdateSQL()及 GetInsertSQL()。这允许你在执行了像"Select * FROM table query Where..."这样的查询函数後,建立一个 $rs->fields复本,改变这些栏位,然後自动产生出更新或是新增的SQL指令。
以下我们展示如何运用这些函数,我们将存取一个资料表,带有下列栏位:(ID,FirstName,LastName,Created)。在这些函数被执行前,你需要藉由一个对资料表的查询指令(select)来初始化一个资料集。
<?
#==============================================
# GetUpdateSQL() 及 GetInsertSQL() 范例码
#==============================================
include('ADOdb.inc.php');
include('tohtml.inc.php');
#==========================
# 以下的程式码测试新增状态
$sql = "Select * FROM ADOXYZ Where id = -1";
# 从资料库中查询出一个空的资料集
$conn = &ADONewConnection("mysql"); # 建立一个连结
$conn->debug=1;
$conn->PConnect("localhost", "admin", "", "test"); # 连结到 MySQL, 资料库名称为 test
$rs = $conn->Execute($sql); # 执行查询,并取得一个空的资料集
$record = array(); # 初始化一个阵列,以便存放记录资料供新增用
# 设定记录中的栏位值
$record["firstname"] = "Bob";
$record["lastname"] = "Smith";
$record["created"] = time();
# 传入空的资料集及栏位资料阵列到GetInsertSQL函数中,以执行功能
# 这个函数将会依传入的资料,回传一个全格式的 Insert SQL指令
$insertSQL = $conn->GetInsertSQL($rs, $record,true);
/************************************************
GetInsertSQL(&$rs, $arrFields,$magicq=false)
建立一个 SQL 以新增一笔记录到被给予的资料集 $rs。这个查询必需是在连结状态。$magicq 被用于指出魔术引号功能是否被激活
************************************************/
$conn->Execute($insertSQL); # 将记录挿入资料库中
#==========================
# 以下的程式码测试更新状态
$sql = "Select * FROM ADOXYZ Where id = 1";
# 选择一笔记录以便更新
$rs = $conn->Execute($sql); # 执行这个查询,并取得一个存在的记录来更新
$record = array(); # 初始化一个阵列,以存放要更新的资料
# 设定栏位里的值
$record["firstname"] = "Caroline";
$record["lastname"] = "Smith"; # 更新 Caroline的姓由 Miranda 变成 Smith
# 传入这个只有单一记录的资料集以及含有资料的阵列到 GetUpdateSQL函数里
# 函数将会回传一个具有正确 Where 条件的 Update(更新) SQL 指令
$updateSQL = $conn->GetUpdateSQL($rs, $record, false ,true);
/************************************************
GetUpdateSQL($rs, $arrFields, $forceUpdate=false,$magicq=false)
建立一个 SQL 以更新一个被给予的资料集 $rs ,被修改的字段存放在数组 $arrFields中(这个数组必需是具名数组,字段名为索引,值为修正值),会与原来的资料集做一个比较,如果 $forceUpdate被设为 true,那么即使 $arrFields与 $rs->fields完全相同,也会产生出更新的SQL指令。资料集必需在连结状态。$magicq 被用于指出魔术引号功能是否被激活。
************************************************、
$conn->Execute($updateSQL); # 更新资料库中的记录
$conn->Close();
?>
范例 8: 使用上一笔及下一笔实作卷动
我们使用HTTP取得 $next_page 变数,以追踪要跳去那一页并且储存目前页码在 session 变数 $curr_page 里。
我们呼叫连结物件的 PageExecute()函收去取得我们要的资料集,然後我们使用资料集的 AtFirstPage() 及 AtLastPage() 函数去决定是否显示下一页和上一页按钮。
<?php include_once('ADOdb.inc.php'); include_once('tohtml.inc.php'); session_register('curr_page'); $db = NewADOConnection('mysql'); $db->Connect('localhost','root','','xphplens'); $num_of_rows_per_page = 10; $sql = 'select * from products'; if (isset($HTTP_GET_VARS['next_page'])) $curr_page = $HTTP_GET_VARS['next_page']; if (empty($curr_page)) $curr_page = 1; ## at first page $rs = $db->PageExecute($sql, $num_of_rows_per_page, $curr_page); if (!$rs) die('Query Failed'); if (!$rs->EOF && (!$rs->AtFirstPage() || !$rs->AtLastPage())) { if (!$rs->AtFirstPage()) { ?> <a href="<?php echo $PHPSELF,'?next_page=',$rs->AbsolutePage() - 1 ?>">Previous page</a> <?php } if (!$rs->AtLastPage()) { ?> <a href="<?php echo $PHPSELF,'?next_page=',$rs->AbsolutePage() + 1 ?>">Next page</a> <?php } rs2html($rs); } ?>
以上的程式码可以在 testpaging.php 范例里找到。
使用自定错误处理及 PEAR_Error
在之前的版本,你可以使用像 $con->debug=true ; 这样的设定来进行除错。但在 1.50 版後,我们提供了另一种方法来处理错误状态。我们让工程师可以使用 ADODB 的自订错误处理程序功能。
ADODB 提供了两种自订处理方式,你可以配合你的的需要而修订。第一个方法放在 ADOdb- errorhandler.inc.php 档案里。这让你可以使用标准的 PHP 函数 err_reporting 去控制要显示怎样的错误讯息及 trigger_error 去呼叫 PHP 预设的错误处理程序。
引入了上述档案後(ADOdb-errorhandler.inc.php),当发生了下列的错误後,将会使得 trigger_error($errorstring,E_USER_ERROR)被呼叫。
-
Connect() 或 PConnect() 执行失败时。
-
执行 SQL 指令的函数失败时,如 Execute() 或 SelectLimin() 。
-
GenID() 进入了无限回圈时。
这里的 $errorstring 变数是由 ADODB 所产生的。而且会包含了有用的除错讯息,类似於随後会建立的 error.log 资料。所以,为了要能正确提供除错讯息,你要在建立 ADOConnection 物件前,就把 ADOdb- errorhandler.inc.php 引入到程式码中。
If you define error_reporting(0), no errors will be shown. If you set error_reporting(E_ALL), all errors will be displayed on the screen.
如果你设定了 error_reporting(0) 的话,将不会有任何错误被显示。如果你设定了 error_reporting(E_ALL),那将会显示所有的错误讯息。
以下是一个简单的范例:
<?php error_reporting(E_ALL); # 显示所有的错误讯息 include('ADOdb-errorhandler.inc.php'); include('ADOdb.inc.php'); include('tohtml.inc.php'); $c = NewADOConnection('mysql'); $c->PConnect('localhost','root','','northwind'); $rs=$c->Execute('select * from productsz'); #不正确的资料表 productsz'); if ($rs) $rs2html($rs); ?>
如果你要把错误讯息记录下来,你可以定义两个选择性常数 ADODB_ERROR_LOG_TYPE, ADODB_ERROR_LOG_DEST。有关於 ADODB_ERROR_LOG_TYPE 的值,你可以去叁考 PHP 使用手册中有关於 error_log 的说明。在以下的范例中,我使将它设为 3,意思是指将讯息记录到常数 ADODB_ERROR_LOG_DEST 所设定的档案中。
<?php error_reporting(0); # 不显示任何的错误讯息 define('ADODB_ERROR_LOG_TYPE',3); define('ADODB_ERROR_LOG_DEST','C:/errors.log'); include('ADOdb-errorhandler.inc.php'); include('ADOdb.inc.php'); include('tohtml.inc.php'); $c = NewADOConnection('mysql'); $c->PConnect('localhost','root','','northwind'); $rs=$c->Execute('select * from productsz'); ## 不正确的资料表 productsz if ($rs) $rs2html($rs); ?>
以下则是写在 error.log 档的错误讯息:
(2001-10-28 14:20:38) mysql error: [1146: Table 'northwind.productsz' doesn't exist] in EXECUTE("select * from productsz")
第二种错误处理方法是 ADOdb-errorpear.inc.php 。使用这种方式,在错误发生时会产生 PEAR_Error 衍生物件,而最後产生的 PEAR_Error 物件可以被 ADODB_Pear_Errir() 函数取回。
<?php include('ADOdb-errorpear.inc.php'); include('ADOdb.inc.php'); include('tohtml.inc.php'); $c = NewADOConnection('mysql'); $c->PConnect('localhost','root','','northwind'); $rs=$c->Execute('select * from productsz'); #不正确的资料表 productsz'); if ($rs) $rs2html($rs); else { $e = ADODB_Pear_Error(); echo '<p>',$e->message(),'</p>'; } ?>
在引入 ADOdb-errorpear.inc.php 档之前,藉由定义 ADODB_PEAR_ERROR_CLASS 常数,你可以使用一个 PEAR_Error 衍生类别。为了方便除错,你可以在 PHP 程式码的最前面定义预设的错误理方式为 PEAR_ERROR_DIE,这将会使得程式一出错,马上就输出错误讯息,并且停止执行。
include('PEAR.php'); PEAR::setErrorHandling('PEAR_ERROR_DIE');
注意,当错误产生时,ADODB并没有明确的回传一个 PEAR_Error 物件给你。你必需要去呼叫 ADODB_Pear_Error() 函数去取回最後的错误内容。或者,你可以使用 PEAR_ERROR_DIE 这个技巧。
资料集快取
现在,ADODB使用 CacheExecute(),CachePageExecute()及CacheSelectLimit()函数来支援资料集快取。用法类似於没有快取的函数,除了要加上一个新的叁数 $secs2cache。
以下是一个范例 :
include('ADOdb.inc.php'); # 载入ADODB $ADODB_CACHE_DIR = '/usr/ADODB_cache'; $conn = &ADONewConnection('mysql'); # 建立一个连结 $conn->PConnect('localhost','userid','','agora');# 连结到 MySQL, agora 资料库 $sql = 'select CustomerName, CustomerID from customers'; $rs = $conn->CacheExecute(15,$sql);
第一个叁数是设定查询的快取秒数。随後呼叫的查询将会使用存放在由 $ADODB_CACHE_DIR 变数指定的快取资料。要强迫查讯执行,并且更新快取记录,使用 CacheExecute() 函数,并且将第一个叁数设为 0 。或者,使用 CacheFlush($sql) 也行。
基於安全的考量,如果你要使用 $ADODB_CACHE_DIR,我们建议你将在 php.ini 里的 register_globals 设成 off。
在 ADODB 1.80版以後,在 CacheSelectLimit() 及 CacheExecute() 中,叁数 secs2cache 是选择性的。如果你不填上去,系统将会使用 $connection-> cacheSecs 属性的值,它的预设值是 60 分钟。
$conn->Connect(...); $conn->cacheSecs = 3600*24; // 快取24小时 $rs = $conn->CacheExecute('select * from table');