直播中
你可能有一個站點(diǎn),有些頁面要處理數(shù)據(jù)庫。你可能想使用PHPLIB,但是不想為了適應(yīng)它而改變現(xiàn)存的
數(shù)據(jù)庫。這兒給出實(shí)現(xiàn)你的夢想的答案。PHPLIB和多個數(shù)據(jù)庫。
實(shí)現(xiàn)它需要對PHPLIB進(jìn)行擴(kuò)充。本文解釋了如何創(chuàng)建擴(kuò)充。你會發(fā)現(xiàn),這篇文章會幫助你在其它方面對
PHPLIB進(jìn)行擴(kuò)充。閱讀完這篇文章后,思考一下PHPLIB可以完成你想要的98%的情況吧。
這篇文章所提及的PHPLIB的擴(kuò)充的建議已經(jīng)提交給了PHPLIB的開發(fā)組。因此,在未來的版本中,可能會
出現(xiàn)這些擴(kuò)充。在你的網(wǎng)頁中的其它的代碼將幫助你組織你的數(shù)據(jù)庫管理。
數(shù)據(jù)庫管理
你可以將每一個表放在一個巨大的數(shù)據(jù)庫中。然而,總有一天會刺傷你。對數(shù)據(jù)庫進(jìn)行管理將會使創(chuàng)傷
減小到最小。當(dāng)你的數(shù)據(jù)庫對一個服務(wù)器來說太大時會出現(xiàn)什么情況?當(dāng)一個服務(wù)器不能處理IO吞吐量或者
沒有足夠的內(nèi)存進(jìn)行處理又會怎么樣?將現(xiàn)有的數(shù)據(jù)庫進(jìn)行拆分很難,但是從分離的數(shù)據(jù)庫開始則容易多了,
并且好的數(shù)據(jù)庫管理會很有幫助。
如果你經(jīng)營一家書店,你可能有作者列表,帶有價格的書目列表,當(dāng)前庫存列表和訂單列表。隨著你的
業(yè)務(wù)的發(fā)展,訂單列表會增加,并且每一個訂單會占用很多的磁盤空間。一種可能性就是有一天你會將訂單
直接放進(jìn)財(cái)務(wù)系統(tǒng)。
現(xiàn)在開始將訂單放在分離的數(shù)據(jù)庫中。因?yàn)閹齑娴臄?shù)量是隨著訂單而變化的,所以將庫存數(shù)據(jù)放在同一
個數(shù)據(jù)庫中。
作者列表和書目列表為一些靜態(tài)信息,它們會經(jīng)常被讀到,但是很少改變。實(shí)際上,唯一的改變可能就
是對于作者記錄會每5年一次,這可能是當(dāng)這個作者寫了一本新書(或死亡了)。這些數(shù)據(jù)可能使用與訂單數(shù)
據(jù)庫完全不同的配置。
包含PHPLIB
PHPLIB通過一個名為DB_Sql的類來操作SQL數(shù)據(jù)庫。在你的代碼中包含適合你的數(shù)據(jù)庫的版本。在這個
例子中,我使用MySQL版本。
為了在你的代碼中得到DB_Sql,在PHPLIB要求的目錄下安裝PHPLIB文件。然后,找到你的cgi-bin目錄,
然后在cgi-bin目錄下創(chuàng)建phplib目錄。接著,拷貝所有的PHPLIB中的.inc文件到phplib目錄下。最后,將
phplib目錄放在php.ini文件中include_path = 的那行上。
include_path是PHP引用在include()或require()中文件名的地方。在我的NT工作站上,包含路徑是
include_path = ".;i:/project52/includes;i:/project52/phplib";
在Linux機(jī)器上,則為
include_path = ".;/home/httpd/includes;/home/httpd/phplib";
在每一個PHP頁面的頂端為
<?php
require(common.php3);
?>
common.php3在包含目錄中,包含對每一頁都通用的所有的數(shù)據(jù)和函數(shù)。在common.php3中,為
<?php
require(db_mysql.inc);
require(ct_sql.inc);
require(session.inc);
require(auth.inc);
require(perm.inc);
require(user.inc);
require(page.inc);
?>
閱讀PHPLIB文檔(http://phplib.netuse.de),也可以在http://www.phpbuilder.com下查找一些好文
章,來了解你需要包括些什么。Db_mysql.inc包含了DB_Sql類的定義。如果你想將MySQL改換成PostGreSQL
數(shù)據(jù)庫,將db_mysql.inc改成對db_pgsql.inc的包含。那里有10個.inc文件,涵蓋了MS SQL,Oracle,Sybase
和其它一些數(shù)據(jù)庫。
請注意,在這個例子中,require()和include()是完全一樣的。Require()和include()工作方式不同,
當(dāng)用在代碼中間或在if()語句中時,結(jié)果是不一樣的。
擴(kuò)充PHPLIB
PHPLIB處理數(shù)據(jù)庫是通過從DB_Sql類創(chuàng)建的一個對象實(shí)現(xiàn)的。Db_mysql.inc包含了DB_Sql類,為MySQL
進(jìn)行了修改。我們將通過向common.php3添加代碼來擴(kuò)充DB_Sql,在包含db_mysql.inc的行的后面。
DB_Sql包含了很多進(jìn)行查詢的函數(shù)。我們想改變的一個是:
<?php
/* public: 連接管理 */
function connect($Database = "", $Host = "", $User = "", $Password = "") {
/* 缺省處理 */
if ("" == $Database)
$Database = $this->Database;
if ("" == $Host)
$Host = $this->Host;
if ("" == $User)
$User = $this->User;
if ("" == $Password)
$Password = $this->Password;
/* 建立連接,選擇數(shù)據(jù)庫 */
if ( 0 == $this->Link_ID ) {
$this->Link_ID=mysql_pconnect($Host, $User, $Password);
if (!$this->Link_ID) {
$this->halt("pconnect($Host, $User, \$Password) failed.");
return 0;
}
if (!@mysql_select_db($Database,$this->Link_ID)) {
$this->halt("cannot use database ".$this->Database);
return 0;
}
}
return $this->Link_ID;
}
?>
在你的db_mysql.inc中找到connnect()函數(shù)(或針對你的數(shù)據(jù)庫的.inc文件),然后將其拷貝到
common.php3中,在db_mysql.inc包含之后的某個地方。你應(yīng)該將其封裝在類的定義中,并按照本文最后所
描述的那樣。
我發(fā)現(xiàn)那段代碼很難讀懂。因此,要做的第一件事就是讓拷貝的代碼可讀:
<?php
/* public: 連接管理 */
function connect($Database = "", $Host = "", $User = "", $Password = "") {
/* 缺省處理 */
if ("" == $Database) {
$Database = $this->Database;
}
if ("" == $Host) {
$Host = $this->Host;
}
if ("" == $User) {
$User = $this->User;
}
if ("" == $Password) {
$Password = $this->Password;
}
/* 建立連接選擇數(shù)據(jù)庫 */
if ( 0 == $this->Link_ID ) {
$this->Link_ID=mysql_pconnect($Host, $User, $Password);
if (!$this->Link_ID) {
$this->halt("pconnect($Host, $User, \$Password) failed.");
return 0;
}
if (!@mysql_select_db($Database,$this->Link_ID)) {
$this->halt("cannot use database ".$this->Database);
return 0;
}
}
return $this->Link_ID;
}
?>
我將代碼進(jìn)行縮近排列,這樣對于包括起來的代碼,層次關(guān)系可以讓我對括號(譯注:指大括號)進(jìn)行
匹配。這樣做可以避免象因?yàn)閬G失括號而引起的錯誤。對于單獨(dú)的行我也增加了括號。PHP允許你當(dāng)if語句后
只有單一代碼行時不使用括號。一旦你添加了額外的代碼,這個簡寫就失敗了。我建議總是加上括號,以免
在后面增加代碼時出現(xiàn)錯誤。
現(xiàn)在,該修改connect代碼了。注意connect()代碼是如何檢測一個連接的存在,并且當(dāng)連接不存在時是
如何創(chuàng)建連接的。這個connect()函數(shù)在每一個數(shù)據(jù)庫查詢之前被調(diào)用。不幸的是,當(dāng)創(chuàng)建連接時它只選擇
一次數(shù)據(jù)庫。如果PHP頁面使用一個以上的數(shù)據(jù)庫,connect()代碼將不會看到數(shù)據(jù)庫的變化。
有幾種方式可以修改代碼。我們正在尋找對PHPLIB影響最小的修改方法,并且可以在我們需要診斷一個
問題時,可以讓我們顯示活動數(shù)據(jù)庫的狀態(tài)。需要超出PHPLIB的兩個變量是連接id和數(shù)據(jù)庫名稱。因此,使
這兩個變量對PHPLIB外部可見。在common.php3:
<?php
$db_connection = 0; // 普通數(shù)據(jù)庫連接id
$db_database = ""; // 當(dāng)前數(shù)據(jù)庫名字
?>
接著,我們修改PHPLIB來保存連接id和數(shù)據(jù)庫名字在這些字段中。你的其它的代碼可以設(shè)置和使用同一
字段。如果你需要在診斷問題時知道哪一個數(shù)據(jù)庫正在使用,在你的頁面中插入這些代碼:
<?php
Print("<p>db_database: " . $db_database . "</p>");
?>
(有一些更簡潔的方法來書寫打印語句。這個方法可以在彩色代碼編輯器中加亮變量名。這個方法對于
數(shù)組和其它復(fù)合變量名工作也很穩(wěn)定。)
我們?nèi)绾巫宑onnect()來使用新的變量呢?我們可在項(xiàng)部加入額外的行,所以你可以:
<?php
{
globals $db_connect, $db_database;
/* 缺省處理 */
?>
這行使我們的外部變量在connect()中有效。
下面是更傳統(tǒng)些的方法。在$db_database后面直接加入:
<?php
function db_connect($db_connect_host="", $db_connect_user="",$db_connect_pass="") {
globals $db_connect;
if(!empty($db_connect_host)) {
$db_connect = mysql_pconnect($db_connect_host,
$db_connect_user, $db_connect_pass);
}
return($db_connect);
}
function db_database($db_database_new="") {
globals $db_database;
if(!empty($db_database_new)) {
$db_database = @mysql_select_db($db_database_new, db_connect());
}
return($db_database);
}
?>
通過定義這些通用函數(shù)一次,你可以在各種地方得到通用變量,不需要在所有地方增加全局行。這兒就
是使用了我們的db函數(shù)的通用函數(shù):
<?php
function connect($Database = "", $Host = "", $User = "", $Password = "") {
/* 缺省處理 */
if ("" == $Database) {
$Database = $this->Database;
}
if ("" == $Host) {
$Host = $this->Host;
}
if ("" == $User) {
$User = $this->User;
}
if ("" == $Password) {
$Password = $this->Password;
}
/* 建立連接,選擇數(shù)據(jù)庫 */
if ( 0 == db_connect()) {
$this->Link_ID = db_connect($Host, $User, $Password);
if (!$this->Link_ID) {
$this->halt("pconnect($Host, $User, \$Password) failed.");
return 0;
}
}
if (0 != db_connect()) {
if($Database != db_database()) {
$this->Database = db_database($Database))
if(empty($this->Database)) {
$this->halt("cannot use database " . $this->Database);
return 0;
}
}
}
return $this->Link_ID;
}
?>
請注意這些小地方的修改:
對于數(shù)據(jù)庫的測試是在連接測試之外的,以便connect()可以檢測一個新的數(shù)據(jù)庫,甚至當(dāng)已經(jīng)存在一
個當(dāng)前連接的時候。這就是說,我們將db_connect()同0進(jìn)行比較兩次。這樣的結(jié)果值得做一些小改動。
我們將數(shù)據(jù)庫的連接與數(shù)據(jù)庫的選擇放在PHPLIB之外,這樣在代碼中需要進(jìn)行數(shù)據(jù)庫選擇的地方我們可
以使用相同的函數(shù)。
在這種情況下只有一個不好的地方:我們假設(shè)相同的主機(jī),用戶和口令,用于所有的數(shù)據(jù)庫操作。如果
你使用一個用戶登錄,使用特別的權(quán)限處理指定的數(shù)據(jù)庫,你將不得不為這種處理創(chuàng)建一個特別的連接。怎
么辦呢?定義變量:
<?php
$db_host = "";
$db_user = "";
$db_pass = "";
?>
擴(kuò)充db_database()函數(shù),比較當(dāng)前用戶和主機(jī)名同特殊的用戶和主機(jī)。你也可以加入:
<?php
$db_type = "";
?>
然后用它來保存數(shù)據(jù)庫的類型,MySQL,Oracle,等等,這樣你就可以處理多個數(shù)據(jù)庫。
修改代碼以便可以處理多個數(shù)據(jù)庫要復(fù)雜一點(diǎn)。你需要修改查詢函數(shù),還包括連接與選擇函數(shù)。你也許
想閱讀一下關(guān)于PHP的ODBC方式的連接,在PHPLIB中使用ODBC選項(xiàng)。ODBC可以以一種通用的方法處理很多的
數(shù)據(jù)庫,但可能有些慢。ODBC可以允許你使用相同的代碼在多個類型的數(shù)據(jù)庫上。如果你確實(shí)使用了多個數(shù)
據(jù)庫類型,可能會遇到對數(shù)據(jù)格式的要求不同的問題和不同數(shù)據(jù)庫之間的差異的問題。ODBC簡化了連接,但
是沒有完善數(shù)據(jù)庫解釋數(shù)據(jù)和SQL的方法。
現(xiàn)在開始關(guān)于派生對象類的簡短教學(xué)。connect()函數(shù)被封裝在類的定義中:
<?php
class DB_Sql {
}
?>
當(dāng)我們拷貝這個函數(shù)到common.php3中時,我們需要派生DB_Sql類。我們通過封裝connect()來實(shí)現(xiàn):
<?php
class db_DB_Sql extends DB_Sql {
}
?>
可以查閱PHP的文檔關(guān)于對象和類的內(nèi)容,看一下"extends"做了些什么。用最少的話來說就是:在派生
中定義的每個東西替換或覆蓋了原始定義的東西。
現(xiàn)在可以使用db_DB_Sql了。當(dāng)你安裝好PHPLIB時,你會有一條語句,寫為:
<?php
$x = new DB_Sql;
?>
將其改成:
<?php
$x = new db_DB_Sql;
?>
這樣就會使用修改后的類,代替了原始的類。
你現(xiàn)在已經(jīng)成了一個對象,類,OOP的專家了,可以要求每年薪水為$10,100了。(老外膽子夠大)
我們做了一個有效的修改,而且對PHPLIB代碼的影響最小。記錄下修改的痕跡,這樣你可以將其重用于
PHPLIB的新版本中。如果在數(shù)據(jù)庫處理中出現(xiàn)錯誤,你可以在外部的函數(shù)中插入print語句,看一看在連接
時發(fā)生了什么。現(xiàn)在你可以做更多的事情了,而沒有修改PHPLIB代碼。
如果SQL看上去失敗了,你可以將qurey()函數(shù)從db_mysql.inc中的DB_Sql中拷貝到common.PHP3中的
db_DB_Sql中去,然后插入print語句,查看SQL的使用情況。
PHPLIB會記錄cookie。在PHPLIB中間的一條print語句可能會產(chǎn)生錯誤消息,是關(guān)于輸出HTTP頭信息的
問題??梢院雎藻e誤,或者將診斷信息寫到一個磁盤文件中去。
開始為:
$db_log_file = "t:/diag.txt";
或相似的語句,用來指向一個在磁盤某個地方的文件。在Windows下,要確信使用了一個存在的目錄,
否則你會得到一個奇怪的錯誤。
現(xiàn)在定義:
<?php
function db_log($db_log_message) {
globals $db_log_file;
$db_log_f = fopen($db_log_file, "a");
fwrite($db_log_f, date("Y m d H:i:s")." ".$db_log_message."\r\n");
fclose($db_log_f);
}
?>
任何時候你需要查看發(fā)生了什么,象這樣加入日志信息:
<?php
db_log("current database: " . db_database());
?>
你可以使用一些內(nèi)建的日志技術(shù)和系統(tǒng)日志。使用系統(tǒng)日志時,可能因?yàn)闆]有處理正確的目錄,從而可
能要搜索大量的文件,卻只為一點(diǎn)點(diǎn)信息。這種分離的日志可以在測試過程中向你提供一些控制。我建議在
操作前后加入日志,象:
<?php
db_log("current database: " . db_database());
db_database("bookcatalogue");
db_log("current database: " . db_database());
?>
記住在你的數(shù)據(jù)庫處理中使用正確的數(shù)據(jù)庫,這樣你就不用查詢PHPLIB數(shù)據(jù)庫了。你可能喜歡為數(shù)據(jù)庫
函數(shù)函數(shù)創(chuàng)建一個封裝函數(shù),或者修改你使用的函數(shù)。如果你使用mysql_query(),你可以首先使用
db_database()。你也可以替換:
<?php
db_database("bookcatalogue");
$result = mysql_query("select * from?", db_connect());
?>
成
<?php
$result = mysql_db_query(db_database("bookcatalogue"), "select * from?",
db_connect());
?>
建議做成函數(shù):
<?php
function db_query($db_query_database, $db_query_sql) {
return(mysql_db_query(db_database($db_query_database), $db_query_sql,
db_connect());
}
?>
現(xiàn)在你可以實(shí)現(xiàn)
使用PHPLIB(和任何相似的軟件)處理多個數(shù)據(jù)庫
擴(kuò)充類/對象
插入診斷檢查
將日志信息寫入文件中
以相反的次序?qū)嵺`它們。在日志文件可工作后,然后是診斷檢查,然后是對類的擴(kuò)充,然后是多個數(shù)據(jù)庫。
令人愉快的代碼。
轉(zhuǎn)自:PHPBuilder.com