close
 轉載~~

-- PHP5新增功能說明,中譯本(一)
PHP5正式發佈
PHP5 新特性(總概)
PHP5 新函數
PHP5 對像體系
PHP5 的XML新特性

-- PHP5新增功能說明,中譯本(二)
PHP5 新特性(ZT)
SQLite in PHP

--------------------------------------------------------------------------------

期待已久的PHP5終於在當地時間7月13日正式發佈了。無論對於PHP語言本身還是PHP的用戶來講,PHP5發佈都算的上是一個里程碑式的版本。在PHP5發佈之前的各個PHP版本就以簡單的語法、豐富的庫函數以及極快的腳本解釋執行速度,贏得了許多開發者的青睞,幾乎成了*NIX平台上首選的WEB開發語言。然而,站在語言本身角度,PHP的語法,特別是OO方面的語法設計並不完善,當然這和PHP語言的作者一開始的設計目的有關。眾說周知,PHP最開始只是一個用Perl寫成的一個模板系統,其後的發展思路也是盡可能為快速開發WEB程序提供方便。大量的庫函數加入其中,而語言模型的發展則相對緩慢。雖然在PHP4中加入了面向對象的設計,但其語言模型並不完善,缺乏諸如構造函數、析構函數、抽像類(接口)、異常處理等基本元素。這極大限制了利用PHP來完成大規模應用程序的能力。

而PHP5的誕生,則從根本上改變了PHP的上述弊端。Zend II 引擎的採用,完備對像模型、改進的語法設計。終使得PHP成為一個設計完備、真正具有面向對像能力的腳本語言。我們預計,PHP5的出現,將會給整個PHP社區帶來新的一輪各種庫、應用開發改進的高峰。

PHP5 新特性(總概)
PHP5.0版本強化了以下功能。首先,完全實現面向對象。PHP4版本中以深拷貝為缺省值的對象代入,在PHP5中變為以淺拷貝為缺省值。因此,PHP5並非與PHP4完全兼容,不過,提供名為PHP兼容模式的功能。
其次是XML功能,PHP5.0版本支持可直觀地訪問XML數據、名為SimpleXML的XML處理用界面。同時還強化了XML Web服務支持,而且標準支持SOAP擴展模塊。
數據庫方面,PHP新版本提供旨在訪問MySQL的新界面--MySQLi。除此前的界面外,還可以使用面向對像界面和預處理語句(Prepared Statement)等MySQL的新功能。另外,PHP5.0上還捆綁有小容量RDBMS--SQLite。

PHP5 新函數

來源 / Zend 翻譯 / EasyChen@phpe


PHP5中新添了一些函數,下邊是它們的列表:

Arrays:

array_combine() - 使用一個數組作為索引,另一個數組作為值來創建一個新數組

array_diff_uassoc() - 使用用戶提供的回調函數比較數組的相異程度(使用額外的索引檢查)

array_udiff() - 使用回調函數比較數組的相異程度

array_udiff_assoc() - 比較數組的相異程度(使用額外的索引檢查)。數據通過回調函數比較。

array_udiff_uassoc() - 比較數組的相異程度(使用額外的索引檢查)。數據通過回調函數比較;索引檢查也由回調函數完成。

array_walk_recursive() - 調用一個用戶定義函數對數組的每個元素進行遍歷。

InterBase:

ibase_affected_rows() - 返回前一個query中生效的row數

ibase_backup() - 在service manager中初始化一個backup任務,並立即返回

ibase_commit_ret() - 在不關閉的情況下,Commit一個事務

ibase_db_info() - 取得數據庫的狀態

ibase_drop_db() - Drop一個數據庫

ibase_errcode() - 返回錯誤代碼

ibase_free_event_handler() - 取消一個已註冊的事件句柄

ibase_gen_id() - 添加named generator並返回新值

ibase_maintain_db() - 執行維護命令

ibase_name_result() - 將一個名字指定到一個結果集

ibase_num_params() - 返回prepared query中參數的個數

ibase_param_info() - 返回一個prepared query中一個參數的信息

ibase_restore() - 在service manager中初始化一個restore任務並立即返回

ibase_rollback_ret() - Rollback一個事務,並保留事務的上下文(context)

ibase_server_info() - 取得一個數據庫的信息

ibase_service_attach() - 連接到service manager

ibase_service_detach() - 和service manager斷開

ibase_set_event_handler() - 註冊一個事件的回調函數

ibase_wait_event() - 等待數據庫事件

iconv:

iconv_mime_decode() - 解碼一個MIME頭

iconv_mime_decode_headers() - 對多個MIME頭一次解碼

iconv_mime_encode() - 形成一個MIME頭

iconv_strlen() - 返回字符串的字符數

iconv_strpos() - 查找字符在串中第一次出現的位置

iconv_strrpos() - 查找字符在串中最後一次出現的位置

iconv_substr() - 取得字符串的一部分

Streams:

stream_copy_to_stream() - 在stream間複製數據

stream_get_line() - 用指定分隔符從stream中取得行

stream_socket_accept() - 接受由stream_socket_server() 創建的socket連接

stream_socket_client() - 打開 Internet 或者 Unix domain socket連接

stream_socket_get_name() - 取得本地或者遠程 sockets的名字

stream_socket_recvfrom() - 從socket接受數據,無論是否連接

stream_socket_sendto() - 發送信息給一個socket,無論是否連接

stream_socket_server() - 創建一個Internet或者Unix domain socket

Other:

convert_uudecode() - 解碼一個通過uuencode的字符

convert_uuencode() - 編碼一個字符為uuencode

curl_copy_handle() - 複製一個cURL句柄,連同它的全部偏好(preferences)

dba_key_split() - 將一個鍵值(key)從string形式分割為數組形式

dbase_get_header_info() - 取得一個dBase數據庫的頭信息

dbx_fetch_row() - 從一個DBX_RESULT_UNBUFFERED標誌置位的query-result取得row

fbsql_set_password() - 改變指定用戶的密碼

file_put_contents() - 將一個string寫入一個文件

ftp_alloc() - 為將被上傳的文件分配空間

get_declared_interfaces() - 返回一個包含了所有已聲明的接口的數組

get_headers() - 取得服務器對一個HTTP請求返回的全部頭數據

headers_list() - 返回發送的(或者準備發送的)頭信息列表

http_build_query() - 生成一個URL編碼的query串

idate() - 將本地時間/日期格式化為整型

image_type_to_extension() - 取得由getimagesize(), exif_read_data(), exif_thumbnail(), exif_imagetype() 生成的圖像類型的文件擴展名

imagefilter() - 自定義圖像過濾

imap_getacl() - 取得指定mailbox的ACL

ldap_sasl_bind() - 使用SASL綁定LDAP目錄

mb_list_encodings() - 返回所有支持的編碼列表數組

pcntl_getpriority() - 取得進程的屬性

pcntl_wait() - 等待或返回fork子進程的由waitpid() 系統調用定義的狀態

pg_version() - 返回一個客戶端,協議和服務器版本的數組

php_check_syntax() - 檢查指定文件的語法

php_strip_whitespace() - 返回去除了註釋和空白格的源代碼

proc_nice() - 改變當前進程的屬性

pspell_config_data_dir() - 更改語言數據文件

pspell_config_dict_dir() - 更改主單詞列表

setrawcookie() - 發送一個不帶URL編碼值得cookie

snmp_read_mib() - 讀取和解析一個MIB文件到MIB樹

sqlite_fetch_column_types() - 返回一個包含特定表的列屬性的數組

str_split() - 將字符轉化為數組

strpbrk() - 查找一個串中字符的任何子集

substr_compare() - 字符串比較,二進制安全

time_nanosleep() - 延遲N納秒

 

 

PHP5 對像體系

* 本文是對《Classes and Objects in PHP5》系列文章的補充和修正,介紹了PHP5對像體系的總體框架,但有些特性沒有具體介紹。強烈建議在讀過《Classes and Objects in PHP5》後閱讀本文。

 

PHP5推出的對象體系相信是大家最為期待的。PHP5借鑒了Java2的對象模型,提供了較為強大的面向對像編程支持,使用PHP來實現OO將變得輕鬆和自然。

 

對像傳遞

 

PHP5使用了Zend引擎II,對像被儲存於獨立的結構Object Store中,而不像其它一般變量那樣儲存於Zval中(在PHP4中對像和一般變量一樣存儲於Zval)。在Zval中僅存儲對象的指針而不是內容(value)。當我們複製一個對像或者將一個對像當作參數傳遞給一個函數時,我們不需要複製數據。僅僅保持相同的對象指針並由另一個zval通知現在這個特定的對象指向的Object Store。由於對像本身位於Object Store,我們對它所作的任何改變將影響到所有持有該對像指針的zval結構----表現在程序中就是目標對象的任何改變都會影響到源對象。.這使PHP對像看起來就像總是通過引用(reference)來傳遞,因此PHP中對像默認為通過「引用」傳遞,你不再需要像在PHP4中那樣使用&來聲明。

 

垃圾回收機制

某些語言,最典型的如C,需要你顯式地要求分配內存當你創建數據結構。一旦你分配到內存,就可以在變量中存儲信息。同時你也需要在結束使用變量時釋放內存,這使機器可以空出內存給其它變量,避免耗光內存。

PHP可以自動進行內存管理,清除不再需要的對象。PHP使用了引用計數(reference counting)這種單純的垃圾回收(garbage collection)機制。每個對象都內含一個引用計數器,每個reference連接到對象,計數器加1。當reference離開生存空間或被設為NULL,計數器減1。當某個對象的引用計數器為零時,PHP知道你將不再需要使用這個對象,釋放其所佔的內存空間。

例如:

<?php
class Person{
}
function sendEmailTo(){
}

$haohappy = new Person( );
// 建立一個新對像: 引用計數 Reference count = 1
$haohappy2 = $haohappy;
// 通過引用複製: Reference count = 2
unset($haohappy);
// 刪除一個引用: Reference count = 1
sendEmailTo($haohappy2);
// 通過引用傳遞對像:
// 在函數執行期間:
// Reference count = 2
// 執行結束後:
// Reference count = 1

unset($haohappy2);
// 刪除引用: Reference count = 0 自動釋放內存空間

?>

 

以上是PHP5在內存管理上的變化,也許大家不怎麼感興趣。下面我們來看看PHP5中的對象模型和PHP4有什麼具體的不同之處:

 

新增功能

改進功能

 

1) Private and Protected Members 私有和保護類成員(屬性,方法)

2) Abstract Classes and Methods 抽像類和抽像方法

3) Interfaces             接口

4) Class Type Hints  類型指示 =

5) final  final關鍵字 =

6) Objects Cloning  對像複製 =

7) Constructors and Destructors 構造函數和析構函數

8) Class Constants 類常量 =

9) Exceptions 異常處理

10) Static member 靜態類成員

11) __METHOD__ constant __METHOD__常量 =

12) Reflection 反射機制 

 

第1、2、3、7、10請自行查閱本文末尾的《Classes and Objects in PHP5》系列,其中已有詳細介紹,本文中不再講解。第9點異常處理和第12點反射機制內容較為豐富,限於篇幅亦不在文中介紹,請關注即將推出的《PHP & More》電子雜誌第二期,會專門撰文介紹。

 

以下向大家介紹第4、5、6、8、11點語言特性:

 

Class Type Hints 類型指示

 

大家都知道,PHP是一種弱類型的語言。在使用變量前不需要定義,不需要聲明變量的數據類型。這在編程中帶來很多便利,但也帶了一些隱患,特別當變量的類型變化時。在PHP5增加了類型指示,可以在執行過程中自動對類方法的參數類型進行判斷。這類似於Java2中的RTTI,配合reflection可以讓我們很好地控制對象。

 

 

<?php
interface Foo {
function a(Foo $foo);
}

interface Bar {
function b(Bar $bar);
}

class FooBar implements Foo, Bar {
function a(Foo $foo) {
// ...
}

function b(Bar $bar) {
// ...
}
}

$a = new FooBar;
$b = new FooBar;

$a->a($b);
$a->b($b);
?>

 

在強類型語言中,所有變量的類型將在編譯時進行檢查,而在PHP中使用類型指示來對類型的檢查則發生在運行時。如果類方法參數的類型不對,將會報出類似「Fatal error: Argument 1 must implement interface Bar…」這樣的錯誤信息。

 

以下代碼:

<?php
function foo(ClassName $object) {
// ...
}
?>

 

相當於:

<?php
function foo($object) {
if (!($object instanceof ClassName)) {
die("Argument 1 must be an instance of ClassName");
}
}
?>

 

 

final / final關鍵字

 

PHP5中新增加了final關鍵字,它可以加在類或類方法前。標識為final的類方法,在子類中不能被覆寫。標識為final的類,不能被繼承,而且其中的方法都默認為final類型。

Final方法:

<?php
class Foo {
final function bar() {
// ...
}
}
?>

 

Final類:

<?php
final class Foo {
// class definition
}

// 下面這一行是錯誤的
// class Bork extends Foo {}
?>

 

Objects Cloning / 對像複製

前面在內存管理部份說過,PHP5中默認通過引用傳遞對象。像使用$object2=$object1這樣的方法複製出的對象是相互關聯的。如果我們確實需要複製出一個值與原來相同的對象而希望目標對象與源對像沒有關聯(像普通變量那樣通過值來傳遞),那麼就需要使用clone關鍵字。如果還希望在複製的同時變動源對像中的某些部份,可以在類中定一個__clone()函數,加入操作。

 

<?php
//對像複製
class MyCloneable {
static $id = 0;

function MyCloneable() {
$this->id = self::$id++;
}


/*
function __clone() {
$this->address = "New York";
$this->id = self::$id++;
}
*/
}

$obj = new MyCloneable();

$obj->name = "Hello";
$obj->address = "Tel-Aviv";

print $obj->id . "\n";

$obj_cloned = clone $obj;

print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?>

 

以上代碼複製出一個完全相同的對象。

 

然後請把function __clone()這一個函數的註釋去掉,重新運行程序。則會複製出一個基本相同,但部份屬性變動的對象。

 

Class Constants / 類常量

PHP5中可以使用const關鍵字來定義類常量。

 

<?php
class Foo {
const constant = "constant";
}

echo "Foo::constant = " . Foo::constant . "\n";
?>

 

__METHOD__ constant __METHOD__常量

__METHOD__ 是PHP5中新增的「魔術」常量,表示類方法的名稱。
魔術常量是一種PHP預定義常量,它的值可以是變化的,PHP中的其它已經存在的魔術常量有__LINE__、__FILE__、__FUNCTION__、__CLASS__等。

<?php
class Foo {
function show() {
echo __METHOD__;
}
}

class Bar extends Foo {
}

Foo::show(); // outputs Foo::show
Bar::show(); // outputs Foo::show either since __METHOD__ is
// compile-time evaluated token

function test() {
echo __METHOD__;
}

test(); // outputs test
?>

 

 

PHP5 的XML新特性
PHP4 的 XML

早期的PHP版本就已經開始支持XML了,而這只是一個基於SAX的接口,它可以輕鬆的解析任何XML文檔。隨著PHP4中加入了DOMXML擴展模塊,XML被更好的支持了。後來XSLT做為補充被加了進來。在整個PHP4的階段,其它一些功能如HTML,XSLT和DTD驗證也被加到了DOMXML擴展中,不幸的是,由於XSLT和DOMXML擴展始終處於實驗階段,API部分也被不止一次的修改,它們還是不能以默認方式安裝。此外,DOMXML擴展沒有遵循W3C制定的DOM標準,而有自己的命名方法。雖然在PHP4.3中這部分得到了改善並且許多內存洩漏和其它一些功能也得以修復,但它始終沒有發展到一個穩定的階段,一些深入的問題已經幾乎不可能修復。只有SAX擴展被已默認方式安裝,其它的一些擴展從未得到廣泛的使用。

基於所有這些原因,PHP的XML開發者決定在PHP5重寫全部代碼,並遵循使用標準。


PHP5的XML
在PHP5中所有支持XML的部分幾乎全部重新編寫.現在的所有XML擴展都是基於GNOME項目的LIBXML2庫。這將允許在不同的擴展模塊之間互相操作,核心開發者只需要在一個底層的庫上進行開發。例如,複雜的內存管理只實現一次就可以讓所有XML相關擴展得到改善。

除了繼承PHP4中聞名的SAX解析器之外,PHP5還支持遵循W3C標準的DOM和基於LIBXSLT引擎的XSLT。同時還加入了PHP獨有的SimpleXML擴展和符合標準的SOAP擴展。隨著XML越來越被重視,PHP開發者決定在默認安裝方式中加入更多對XML的支持。這就意味著你現在可以使用SAX,DOM和SimpleXML,而這些擴展將會在更多的服務器上安裝。然後對於XSLT和SOAP的支持,還需要在PHP編譯時被顯式的配置。


數據流的支持

現在所有的XML擴展都支持PHP數據流,即使你不從PHP中直接訪問。例如,在PHP5中你可以從一個文件或從一條指令訪問數據流。基本上你能夠在任何可以訪問普通文件的地方訪問PHP數據流。

PHP4.3中簡要的介紹了數據流,在PHP5中已經得到了進一步的提高,包含文件存取,網絡存取和其它操作,如共享一套功能函數。你甚至可以使用PHP代碼來實現你自己的數據流,這樣數據存取將變得非常簡單。關於這部分的更多細節請參考PHP文檔。


SAX

SAX的全稱是Simple API for XML,它是用於解析XML文檔的接口,是基於回調形式的。從PHP3開始就已經支持了SAX,到現在也沒有太大的變化。在PHP5中,API接口並沒有改變,所以你的代碼仍然可以運行。唯一不同的是它不再基於EXPAT庫,而是基於LIBXML2庫。

這個變化帶來了一些對命名空間支持上的問題,這個問題在LIBXML2.2.6版本中已經得到解決。但是LIBXML2以前的版本中並沒有解決,因此如果你使用了xml_parse_create_ns();強烈建議在你的系統上安裝LIBXML2.2.6。


DOM

DOM (文檔對像模型)是由W3C制定的一套訪問XML文檔樹的標準。在PHP4可以使用DOMXML來對此進行操作,DOMXML的最主要問題是它不符合標準的命名方法。而且在很長一段時間內還存在內存洩漏問題(PHP4.3已經修復了這個問題)。

新的DOM擴展是基於W3C標準完成的,包含方法和屬性名稱。如果你在其它語言中熟悉DOM,例如在JavaScript中,那麼在PHP中編寫類似的功能將變得非常容易。你不必每次都查看文檔,因為方法和參數都是相同的。

由於使用了新的W3C標準,基於DOMXML的代碼將不能運行。在PHP中的API有很大的不同。但是如果你的代碼中使用了類似W3C標準的方法命名方式,移植並不是很困難。你只需要將載入函數和保存函數修改,刪除函數名中的下劃線(DOM標準使用首字母大寫)。其它各處的調節當然也是必須的,但是主要邏輯部分可以保持不變。


讀取DOM

我不會在這篇文章中解釋DOM擴展的所有特性,那也是沒有必要的。或許你應該將HTTP://www.w3.org/DOM的文檔加入書籤。它與PHP5的DOM部分基本上相同。

在這篇文章的大多數例子中我們將使用同一個XML文件,zend.com上有非常簡單的RSS版本。將下面的文本粘貼到一個文本文件中並保存為articles.xml。



http://www.zend.com/zend/week/week172.php

http://www.zend.com/zend/tut/tut-hatwar3.php

要將這個例子載入到一個DOM對象,首先要創建一個DOMDocument對象,然後載入XML文件。

$dom = new DomDocument();
$dom->load("articles.xml");

正像上面所提及的,你可以使用PHP的數據流來載入一個XML文檔,你應該這樣寫:

$dom->load("file:///articles.xml");

(或者其它類型的數據流)

如果你想將XML文檔輸出到瀏覽器或做為標準標出,使用:

print $dom->saveXML();

如果你想把它保存成文件,請使用:

print $dom->save("newfile.xml");

(注意這樣做會將文件大小發送到stdout)

當然這個例子沒有太多的功能,讓我們來做些更有用的。我們來取得所有的title元素。有很多方法可以辦到,最簡單的就是使用getElementsByTagName($tagname):

$titles = $dom->getElementsByTagName("title");
foreach($titles as $node) {
print $node->textContent . "\n";
}

textContent屬性並不是W3C標準,它可以讓我們很方便的快速讀取一個元素的所有文本節點,使用W3C的標準讀取是下面這樣:

$node->firstChild->data;

(這時候你要確保firstChild結點是你需要的文本結點,否則你還得遍歷所有子結點來查找)。

另外一個要注意的問題是getElementsByTagName()返回一個DomNodeList,對象,而不是像PHP4中get_elements_by_tagname()那樣返回一個數組,但是正像你在這個例子中看到的那樣,你可以使用foreach語句輕鬆的遍歷它。你也可以直接使用$titles->item(0)來訪問結點。該方法將返回第一個title元素。

另一個取得所有title元素的辦法是從根結點遍歷,你可以看到,這個方法更複雜,但是如果你需要的不只是title元素的時候,這個方法也就更靈活。

foreach ($dom->documentElement->childNodes as $articles) {
//如果節點是一個元素(nodeType == 1)並且名字是item就繼續循環
if ($articles->nodeType == 1 && $articles->nodeName == "item") {
foreach ($articles->childNodes as $item) {
//如果節點是一個元素,並且名字是title就打印它.
if ($item->nodeType == 1 && $item->nodeName == "title") {
print $item->textContent . "\n";
}
}
}
}

XPath

XPaht 就像是XML的SQL,使用XPath你可以在一個XML文檔中查詢符合一些模式語法的特定結點。想使用XPath獲得所有title結點,只需要這麼做:

<?
$xp = new domxpath($dom);
$titles = $xp->query("/articles/item/title");
foreach ($titles as $node) {
print $node->textContent . "\n";
}
?>

這樣和使用getElementsByTagName()方法差不多,但是Xpath要強大的多,例如,如果我們有一個title元素是article的子元素(而不是item的子元素),getElementsByTagName()就會將它返回。而使用/articles/item/title語法,我們只會得到在指定深度和位置的title元素。這只是一個簡單的例子,再深入一點可能是這樣:

/articles/item[position() = 1]/title 返回第一個item元素的所有

/articles/item/title[@id = '23'] 返回所有含有id屬性並且值為23的title

/articles//title 返回所有articles元素下面的title(譯者註://代表任意深度)

你也可以查詢含有特殊兄弟元素的點,含有特殊文本內容的元素,或者使用命名空間等等。如果你必須大量的查詢XML文檔,適當的學習使用XPath會節省你很多時間,它使用簡單,執行速度快,比標準的DOM需要更少的代碼。


向DOM中寫入數據

文檔對像模型並不是只能讀取和查詢,你也可以操作和寫入。(DOM標準有點冗長,因為編寫者想盡量支持能夠想像到的每一個環境,但是它工作的非常好)。看看下面這個例子,它在我們的article.xml文件中添加了一個新元素。

$item = $dom->createElement("item");
$title = $dom->createElement("title");
$titletext = $dom->createTextNode("XML in PHP5");
$title->appendChild($titletext);
$item->appendChild($title);
$dom->documentElement->appendChild($item);
print $dom->saveXML();

首先,我們創建了所有需要的結點,一個item元素,一個title元素和一個包含item標題的文本結點,然後我們將所有的結點鏈接起來,把文本結點加到title元素上,把title元素加到item元素上,最後我們把item元素插入到articles根元素上。現在,我們的XML文檔中有一個新的文章列表了。

擴展類(class)
好了,上面的例子都可以在PHP4下面用DOMXML擴展來做(只是API有一些不同),能夠自己擴展DOM類是PHP5的一個新特性,這使得書寫更多可讀性強的代碼變得可能。下面是用DOMDocument類重新寫的整個例子:

class Articles extends DomDocument {
function __construct() {
//必須調用!
parent::__construct();
}

function addArticle($title) {
$item = $this->createElement("item");
$titlespace = $this->createElement("title");
$titletext = $this->createTextNode($title);
$titlespace->appendChild($titletext);
$item->appendChild($titlespace);
$this->documentElement->appendChild($item);
}
}
$dom = new Articles();
$dom->load("articles.xml");
$dom->addArticle("XML in PHP5");
print $dom->save("newfile.xml");

HTML
PHP5中一個經常不被注意到的特性是libxml2庫對HTML的支持,你不僅可以使用DOM擴展載入結構良好(well-formed)的XML文檔,還可以載入非結構良好的(not-well-formed)HTML文檔,把它當做標準的DOMDocument對象,使用所有能用的方法和特性,比如XPath和SimpleXML。

當你需要訪問一個你無法控制站點的內容時,HTML的性能就顯示十分有用了。在 XPath, XSLT 或 SimpleXML的幫助下,你省掉了許多代碼,像使用正則表達式比較字符串或者SAX解析器。當HTML文檔結構不是很好的時候,這個辦法尤其有用(這是個頻繁的問題!)。

下面的代碼獲得並解析php.net的首頁,將返第一個title元素的內容。

$dom = new DomDocument();
$dom->loadHTMLFile("http://www.php.net/");
$title = $dom->getElementsByTagName("title");
print $title->item(0)->textContent;

請注意當指定元素沒有找到時,你的輸出可能會包含錯誤。如果你的網站還在使用PHP輸出HTML4代碼,有一個好消息要告訴你,DOM擴展不僅能載入HTML文檔,而且還能將他們保存為HTML4格式的文件。在你添加完DOM文檔後,使用$dom->saveHTML()來保存。要注意的是,為了使輸出的HTML代碼符合W3C標準,最好不用使用 整齊的擴展?(tidy extension)。Libxml2 庫支持的HTML並不會考慮到每個可能發生的事情,也不能很好的處理非通用格式的輸入。

驗證
XML文檔的驗證越來越重要了。例如,如果你從一些國外資源中獲得了一個XML文檔,在你處理之前你需要檢驗它是否符合某個確定的格式。幸運的是你不需要在PHP中寫自己的驗證程序,因為你可以使用三個應用最廣泛的標準之一(DTD,XML Schema 或RelaxNG)來完成它。.

DTD是一個產生於SGML時代的標準,缺少一些XML的新特性(如命名空間),而且由於它不是用XML寫的,它也很難被解析和轉換。
XML Schemai是由W3C制定的一個標準,它應用廣泛,幾乎包含了所有驗證XML文檔所需要的內容。
RelaxNG 是複雜的XML Schema標準的對頭,是由自由者組織創建的,由於它比XML Schema更容易實現,越來越多的程序開始支持RelaxNG了
如果你沒有遺留下來的計劃文檔或者非常複雜的XML文檔,那麼使用RelaxNG吧。它書寫和閱讀都比較簡單,越來越多的工具也支持它。甚至還有一個工具叫Trang,它可以從XML範本中自動創建一個RelaxNG文檔。而且只有RelaxNG(和老化的DTDS)被libxml2完全支持,儘管libxml2也即將完全支持ML Schema。

驗證XML文檔的語法相當簡單:

$dom->validate('articles.dtd');
$dom->relaxNGValidate('articles.rng');
$dom->schemaValidate('articles.xsd');
目前,所有這些都只會簡單的返回true或false,錯誤會被做為PHP警告輸出。顯然想返回給用戶友好的信息這並不是一個好主意,在PHP5.0以後的版本裡會有所改善。到底該怎麼實現目前還在討論之中,但是錯誤報告肯定會處理的更好。


SimpleXML
SimpleXML 是PHP的XML家族中最後一個被加入的成員,加入SimpleXML擴展的目的是為了提供一個使用標準對像屬性和迭代器訪問XML文檔的更簡單的方法。該擴展沒有太多的方法,雖然如此它還是相當強大的。從我們的文檔的取得所有title節點比原來需要更少的代碼。

$sxe = simplexml_load_file("articles.xml");
foreach($sxe->item as $item) {
print $item->title ."\n";
}

這是在幹什麼?首先將articles.xml載入到一個SimpleXML對象。然後取得所有$sxe中的item元素,最後$item->title返回title元素的內容,就是這樣。你也可以使用關聯數組查詢屬性,使用: $item->title['id']。

看到了吧,這後面真是太神奇了,有許多不同的辦法可以得到我們想要的結果,例如, $item->title[0]返回和例子中相同的結果,另一方面,foreach($sxe->item->title as $item)只返回第一個title,並不是所有在文檔中的title元素。(就像我在XPath中預期的那樣)。

SimpleXML 實際上是使用了Zend引擎2新特性的第一個擴展。因此也成了這些新特性的測試點,你要知道在開發階段bugs和不可預料的錯誤可不是少數。

除了上面例子中所使用的遍歷所有節點的方法,在SimpleXML中也有一個XPath接口,它為訪問單個結點提供了更簡單的辦法。

foreach($sxe->xpath('/articles/item/title') as $item) {
print $item . "\n";
}

不可否認,這段代碼也不比前面例子中的短,但是提供了更複雜或更深的嵌套XML文檔,你會發現和SimpleXML一起使用XPath會節省你很多的輸入。

向 SimpleXML 文檔寫入數據
你不僅可以解析和讀取SimpleXML,而且還可以改變SimpleXML文檔。至少我們加入一些擴展:

$sxe->item->title = "XML in PHP5 "; //title元素的新內容。
$sxe->item->title['id'] = 34; // title元素的新屬性。
$xmlString = $sxe->asXML(); // 將SimpleXML對像做為序列化的XML字符串返回
print $xmlString;

互用協作性
由於SimpleXML也是基於libxml2庫的,你可以在幾乎不影響速度的情況下輕鬆的將SimpleXML對像轉化成DomDocument對象。(文檔不用進行內部複製),由於這個機制,你擁有了二個對象的最好部分,使用一個適合你手頭工作的工具吧,它是這樣使用的:

$sxe = simplexml_import_dom($dom);
$dom = dom_import_simplexml($sxe);
XSLT
XSLT是用來將XML文檔轉換為其它XML文檔的語言,XSLT本身是用XML編寫的,屬於功能性語言家族,在程序處理上和面對對像語言(像PHP)有所不同。PHP4中有二種XSLT處理器:Sablotron(在廣泛使用的XSLT擴展中)和Libxslt(在domxml擴展中),這兩種API不互相兼容,並且使用方法也不相同。PHP5只支持libxslt處理器,之所以選擇它是因為它是基於Libxml2的,因此也更符合PHP5的XML概念。

理論上將Sablotron綁定到PHP5上也是可能的,但是不幸的是沒人來做。因此,如果你正在使用Sablotron,你不得不在PHP5中切換到libxslt處理器。Libxslt 是帶有JavaScript異常處理支持的Sablotron,甚至可以使用PHP強大的數據流來重新實現Sablotron獨有的計劃處理(scheme handlers)。此外,libxslt 是 最快的XSLT處理器之一,所以你還免費得到了速度的提升。(執行速度是Sablotron的二倍)。

和本文討論的其它擴展一樣,你可以在XSL擴展,DOM擴展和vice versa之間交換XML文檔,實際上,你必須得這麼做,因為EXT/XSL擴展並沒有載入和保存XML文檔的接口,只能使用DOM擴展。一開始學習XSLT轉換,你不需要掌握太多的內容,這裡也不存在W3C標準,因為這個API中從Mozilla「借」過來的。

首先你需要一個XSLT樣式表,將下列文本粘貼到一個新文件並且保存灰articls.xsl

 

然後用PHP腳本調用它::

 

/* 將XML和XSL文檔載入到DOMDocument對像*/
$xsl = new DomDocument();
$xsl->load("articles.xsl");
$inputdom = new DomDocument();
$inputdom->load("articles.xml");

/* 創建XSLT處理器,並導入樣式表*/
$proc = new XsltProcessor();
$xsl = $proc->importStylesheet($xsl);
$proc->setParameter(null, "titles", "Titles");

/* 轉換並輸出XML文檔 */
$newdom = $proc->transformToDoc($inputdom);
print $newdom->saveXML();

?>

上面的例子首先使用DOM的方法load()載入XSLT樣式表articles.xsl,然後創建了一個新的XsltProcessor對象,該對像導到了後面要使用了XSLT樣式表對象,參數可以這樣設置setParameter(namespaceURI, name, value),最後XsltProcessor對像使用transformToDoc($inputdom)開始轉換並返回一個新的DOMDocument對象。

. 這個API的優點在於你可以使用同一個樣式表轉換許多XML文檔,只需要將它載入一次然後重複使用它,因為transormToDoc()函數可以應用於不同的XML文檔。

除了transormToDoc(),還有二個用於轉換的方法:transformToXML($dom)返回一個字符串,transformToURI($dom, $uri)將轉換之後的文檔保存到文件或一個PHP數據流。注意如果你想使用XSLT的一個語法如 或 indent="yes",你不能使用transformToDoc(),因為DOMDocument對像 不能保存該信息,只能當你將轉換後的結果直接保存到字符串或文件中時才能這樣做。

調用PHP函數
XSLT擴展最後一個新加的特性是能夠在XSLT 樣式表內部調用任何PHP函數,主張正統的XML支持者一定不會喜歡這個功能(這樣的樣式表有點複雜,很容易混淆邏輯和設計),在某些地方卻是十分有用的。當涉及到函數時XSLT就變得很有限,即使想實現用不同的語言輸出一個日期也是非常麻煩的。但是使用這個功能,處理這些就和只使用PHP一樣容易。下面是向XSLT添加一個函數的代碼:

 

function dateLang () {
return strftime("%A");
}

$xsl = new DomDocument();
$xsl->load("datetime.xsl");
$inputdom = new DomDocument();
$inputdom->load("today.xml");

$proc = new XsltProcessor();
$proc->registerPhpFunctions();

// 載入文檔並使用$xsl來處理
$xsl = $proc->importStylesheet($xsl);

/* 轉換並輸出XML文檔 */
$newdom = $proc->transformToDoc($inputdom);

print $newdom->saveXML();

?>

下面是XSLT樣式表datetime.xsl,它會調用這個函數。

 

下面是要使用樣式表轉換的XML文檔,today.xml(同理,articles.xml也會得到同樣結果)。

 

上面的樣式表,PHP腳本和所有的XML文件會用當前系統設置的語言輸出星期的名字。你可以給php:function()添加更多的參數,添加的參數會被傳遞給PHP函數。這裡有一個函數php:functionString(),這個函數自動將所有輸入的參數轉換為字符串,所以你不需要在PHP裡進行轉換。

注意你需要在轉換之前調用$xslt->registerPhpFunctions(),否則PHP函數調用將因為安全原因不會被執行(你始終相信你的XSLT樣式表嗎?)。目前訪問系統還沒有實現,也許在將來PHP5的版本中會實現這個功能。

摘要
PHP對XML的支持已經向前邁進了一大步,它符合標準,功能強大,互用協作性強,被作為默認選項安裝,已被授權使用。新加入的SimpleXML擴展提供了簡單快速訪問XML文檔的方法,可以節省你很多的代碼,尤其是當你有結構化文檔或者可以使用強大的XPath時。

感謝libxml2—PHP5 XML擴展所使用的底層庫,使用DTD,RelaxNG或XML Schema驗證XML文檔現在已經被支持了。

XSL支持也得到了翻新,現在使用Libxslt庫,比原來的Sablotron庫在性能上有很大提高,而且,在XSLT樣式表內部調用PHP函數可以讓你寫出更強大的XSLT代碼。

如果你已經在PHP4或其它語言中使用了XML,你會喜歡PHP5的XML特性的,XML在PHP5中有了很大的變化,符合標準,和其它工具,語言是同等的。 

arrow
arrow
    全站熱搜

    yoonow 發表在 痞客邦 留言(0) 人氣()