開始知道 PHP V5 中的對(duì)象
發(fā)表時(shí)間:2024-06-08 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]本文描述 PHP V5 中對(duì)象和類的基礎(chǔ)知識(shí),從最基本的概念一直講到繼承,主要針對(duì)經(jīng)驗(yàn)豐富的面向?qū)ο蟪绦騿T和尚未接觸過對(duì)象的讀者。作為 PHP 程序員,您肯定知道變量和函數(shù)。但類和對(duì)象可能就是另一回事。不定義單個(gè)類,就可以創(chuàng)建完美的系統(tǒng)。但即使您決定在自己的代碼中不使用面向?qū)ο蟮木幊蹋钥赡苄枰?..
本文描述 PHP V5 中對(duì)象和類的基礎(chǔ)知識(shí),從最基本的概念一直講到繼承,主要針對(duì)經(jīng)驗(yàn)豐富的面向?qū)ο蟪绦騿T和尚未接觸過對(duì)象的讀者。
作為 PHP 程序員,您肯定知道變量和函數(shù)。但類和對(duì)象可能就是另一回事。不定義單個(gè)類,就可以創(chuàng)建完美的系統(tǒng)。但即使您決定在自己的代碼中不使用面向?qū)ο蟮木幊,您仍可能需要了解面向(qū)ο蟮木幊獭@,如果使用第三方庫,比如通過 PHP Extension and Application Repository (PEAR) 可以使用的庫,您將發(fā)現(xiàn)自己在實(shí)例化對(duì)象和調(diào)用方法。
什么是類和對(duì)象?
簡(jiǎn)單地說,類 是一個(gè)由變量和方法組成的獨(dú)立塊或束。這些組件通常結(jié)合實(shí)現(xiàn)單個(gè)責(zé)任或一組責(zé)任。在本文中,您將創(chuàng)建一個(gè)類,該類收集了用于查詢和填充由項(xiàng)和值組成的詞典的方法。
類可以直接用作組織數(shù)據(jù)和功能的簡(jiǎn)單方法,就像一組函數(shù)和變量一樣。但使用類可以忽略它的存在。類可用于在內(nèi)存中生成多個(gè)實(shí)例。這樣的實(shí)例叫做對(duì)象。每個(gè)對(duì)象可以訪問一組相同的函數(shù)(在面向?qū)ο笊舷挛闹薪凶?i xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">方法)和變量(叫做特性或實(shí)例變量),但每個(gè)變量的實(shí)際值在每個(gè)對(duì)象中是不同的。
考慮角色扮演游戲中的一個(gè)單元——比如坦克。類可能為坦克設(shè)置一組變量:防御和進(jìn)攻能力,范圍,健康狀況,等等。該類也可能定義一組函數(shù),其中包括 move()
和 attack()
。當(dāng)系統(tǒng)包含一個(gè)坦克類時(shí),該類可用于生成數(shù)十個(gè)或數(shù)百個(gè)坦克對(duì)象,每個(gè)對(duì)象都潛在地具有自己的健康狀況或范圍特征。因此,類是用于生成對(duì)象的藍(lán)圖或模板。
理解類和對(duì)象最簡(jiǎn)單的方法可能就是創(chuàng)建一些類和對(duì)象。
第一個(gè)類
可以用 class
關(guān)鍵字創(chuàng)建類。最簡(jiǎn)單的情況是,類由關(guān)鍵字類、名稱和代碼塊組成:
類名可以包含字母、數(shù)字和下劃線字符的任何組合,但不能以數(shù)字打頭。
上例中的 Dictionary
類盡管用處有限,但完全合法。那么如何使用該類來創(chuàng)建一些對(duì)象呢?
$obj1 = new Dictionary();$obj2 = new Dictionary();$obj3 = new Dictionary();
|
至少在形式上,實(shí)例化對(duì)象與調(diào)用函數(shù)相似。對(duì)于函數(shù)調(diào)用,必須提供圓括號(hào)。與函數(shù)一樣,一些類需要您為其傳遞參數(shù)。您還必須使用 new 關(guān)鍵字。這就告訴 PHP 引擎您希望實(shí)例化一個(gè)新對(duì)象。然后,返回的對(duì)象可以存儲(chǔ)在一個(gè)變量中以供將來使用。
屬性
在類的主體中,可以聲明叫做屬性的特殊變量。在 PHP V4 中,屬性必須用關(guān)鍵字 var
調(diào)用。這仍是合法的語法,但主要是為了向后兼容。在 PHP V5 中,屬性必須聲明為 public、private 或 protected?梢栽 關(guān)鍵字:在此我們是否可以有一點(diǎn)隱私?中閱讀有關(guān)這些限定詞的內(nèi)容。但現(xiàn)在在例子中將所有屬性聲明為 public。清單 1 顯示一個(gè)聲明了兩個(gè)屬性的類。
清單 1. 聲明兩個(gè)屬性的類class Dictionary { public $translations = array(); public $type ="En";}
|
正如所看到的,可以同時(shí)聲明屬性并為其賦值?梢杂 print_r()
函數(shù)快速瀏覽一下對(duì)象的狀態(tài)。清單 2 顯示 Dictionary
對(duì)象現(xiàn)在具有更多成員。
清單 2. Dictionary
對(duì)象一覽$en = new Dictionary();print_r( $en ); 如果運(yùn)行該腳本,將看到如下對(duì)象的輸出:Dictionary Object( [translations] => Array ( ) [type] => En)
|
可以使用對(duì)象操作符 ->
訪問公共對(duì)象屬性。所以 $en->type
表示由 $en 引用的 Dictionary
對(duì)象的 $type 屬性。如果可以訪問屬性,就意味著可以設(shè)置和獲得其值。清單 3 中的代碼創(chuàng)建 Dictionary
類的兩個(gè)實(shí)例 —— 換言之,它實(shí)例化兩個(gè) Dictionary
對(duì)象。它更改一個(gè)對(duì)象的 $type
屬性,并添加兩個(gè)對(duì)象的翻譯:
清單 3. 創(chuàng)建 Dictionary
類的兩個(gè)實(shí)例$en = new Dictionary();$en->translations['TREE'] = "tree";$fr = new Dictionary();$fr->type = "Fr";$fr->translations['TREE'] = "arbre";foreach ( array( $en, $fr ) as $dict ) { print "type: {$dict->type} "; print "TREE: {$dict->translations['TREE']}\n";}
|
該腳本輸出如下
type: En TREE: treetype: Fr TREE: arbre
|
所以 Dictionary
類現(xiàn)在比較有用了。單個(gè)對(duì)象可以存儲(chǔ)不同的鍵值組合,還有一個(gè)標(biāo)志,該標(biāo)志告訴客戶端有關(guān)這種 Dictionary
的詳細(xì)信息。
盡管 Dictionary
類當(dāng)前與關(guān)聯(lián)數(shù)組的包裝器相差無幾,但這里有一些了解對(duì)象功能的線索。目前,我們已經(jīng)可以很好地表示我們的示例數(shù)據(jù)了,如清單 4 所示。
清單 4. 示例數(shù)據(jù)$en = array( 'translations'=>array( 'TREE' => 'tree' ), 'type'=>'En');$fr = array( 'translations'=>array( 'TREE' => 'arbre' ), 'type'=>'Fr');
|
雖然該數(shù)據(jù)結(jié)構(gòu)完成了與 Dictionary
類相同的目的,但它沒有提供結(jié)構(gòu)的保證。如果傳遞 Dictionary
對(duì)象,我們知道它具有 $translations
屬性。但如果是一個(gè)關(guān)聯(lián)數(shù)據(jù),則沒有這樣的保證。這個(gè)事實(shí)使得類似 $fr['translations']['TREE'];
的查詢有些碰運(yùn)氣,除非進(jìn)行查詢的代碼確定數(shù)組的起源。這是對(duì)象的重點(diǎn):對(duì)象的類型是其特征的保證。
雖然用對(duì)象存儲(chǔ)數(shù)據(jù)有優(yōu)點(diǎn),但是您可能沒有一點(diǎn)感覺。對(duì)象可以是東西,但關(guān)鍵在于它們還可以做事情。
方法
簡(jiǎn)單地說,方法是在類中聲明的函數(shù)。它們通常(但不總是)通過對(duì)象實(shí)例使用對(duì)象操作符來調(diào)用的。清單 5 向 Dictionary
類中添加一個(gè)方法,并調(diào)用該方法。
清單 5. 向 Dictionary
類中添加方法class Dictionary { public $translations = array(); public $type ="En"; function summarize() { $ret = "Dictionary type: {$this->type}\n"; $ret .= "Terms: ".count( $this->translations )."\n"; return $ret; }}$en = new Dictionary();$en->translations['TREE'] = "tree";print $en->summarize();
|
它提供如下輸出:
Dictionary type: EnTerms: 1
|
正如所看到的,聲明 summarize()
方法與聲明任何函數(shù)的方式一樣,只不過它是在類中聲明。summarize()
方法是通過 Dictionary
實(shí)例使用對(duì)象操作符調(diào)用的。summarize()
函數(shù)訪問屬性來提供對(duì)象狀態(tài)的簡(jiǎn)述。
注意對(duì)于本文來說的一個(gè)新特性的用法。$this
偽變量提供了一種用于對(duì)象引用自己的屬性和方法的機(jī)制。在對(duì)象外部,可以使用句柄來訪問它的元素(在本例子中是 $en
)。在對(duì)象內(nèi)部,則無此句柄,所以必須求助于 $this
。如果覺得 $this
有些迷惑,則在代碼中遇到它時(shí),試著在頭腦中用當(dāng)前實(shí)例 替換它。
類通常使用通用建模語言 (Universal Modeling Language,UML) 表示在圖表中。UML 的詳細(xì)信息超出了本文的范圍,但這種圖表不過是一種可視化類關(guān)系的好方法。圖 1 顯示用 UML 表示的 Dictionary
類。類名位于頂層,屬性在中間,方法在底層。
圖 1. 使用 UML 顯示 Dictionary 類
構(gòu)造函數(shù)
PHP 引擎識(shí)別許多“魔術(shù)”方法。如果定義了方法,則 PHP 引擎將在相應(yīng)的情況發(fā)生時(shí)自動(dòng)調(diào)用這些方法。最常實(shí)現(xiàn)的方法是構(gòu)造函數(shù)方法。PHP 引擎在實(shí)例化對(duì)象時(shí)調(diào)用構(gòu)造函數(shù)。對(duì)象的所有基本設(shè)置代碼都放在構(gòu)造函數(shù)中。在 PHP V4 中,通過聲明與類同名的方法來創(chuàng)建構(gòu)造函數(shù)。在 V5 中,應(yīng)聲明叫做 __construct()
的方法。清單 6 顯示需要 DictionaryIO
對(duì)象的構(gòu)造函數(shù)。
清單 6. 需要 DictionaryIO
對(duì)象的構(gòu)造函數(shù)class Dictionary { public $translations = array(); public $type; public $dictio; function __construct( $type, DictionaryIO $dictio ) { $this->type = $type; $this->dictio=$dictio; } //...
|
要實(shí)例化 Dictionary
對(duì)象,需要將類型字符串和 DictionaryIO
對(duì)象傳遞給它的構(gòu)造函數(shù)。構(gòu)造函數(shù)使用這些參數(shù)來設(shè)置自有屬性。下列代碼顯示可以如何實(shí)例化 Dictionary
對(duì)象:
$en = new Dictionary( "En", new DictionaryIO() );
|
Dictionary
類現(xiàn)在比以前更安全。所有 Dictionary
對(duì)象都已經(jīng)用必需的參數(shù)初始化過了。
當(dāng)然,還無法阻止一些人隨后更改 $type
屬性或?qū)?$dictio
設(shè)置為空?上驳氖牵琍HP V5 可以幫助您實(shí)現(xiàn)這一功能。
關(guān)鍵字:在此我們是否可以有一點(diǎn)隱私?
前面已經(jīng)看到與屬性聲明相關(guān)的 public 關(guān)鍵字。該關(guān)鍵字表示屬性的可見度。事實(shí)上,屬性的可見度可以設(shè)置為 public、private 和 protected。聲明為 public 的屬性可以在類外部寫入和讀取,聲明為 private 的屬性只在對(duì)象或類上下文中可見。聲明為 protected 的屬性只能在當(dāng)前類及其子類的上下文中可見。(在 繼承 部分將會(huì)看到這些內(nèi)容起作用。)可以使用 private 屬性來真正鎖定類。如果將屬性聲明為 private 并試圖從類范圍外部訪問它(如清單 7 所示),PHP 引擎將拋出致命錯(cuò)誤。
清單 7. 試圖從類范圍外部訪問屬性class Dictionary { private $translations = array(); private $dictio; private $type; function __construct( $type, DictionaryIO $dictio ) { $this->type = $type; $this->dictio = $dictio; } // ...}$en = new Dictionary( "En", new DictionaryIO() );$en->dictio = null;
|
輸出如下:
Fatal error: Cannot access private property Dictionary::$dictio in...
|
一般來說,應(yīng)將大多數(shù)屬性聲明為 private,然后根據(jù)需要提供獲得和設(shè)置這些屬性的方法。這樣就可以控制類的接口,使一些數(shù)據(jù)只讀,在將參數(shù)分配給屬性之前對(duì)參數(shù)進(jìn)行清理或過濾,并提供與對(duì)象交互的一套明確的規(guī)則。
修改方法可見度的方法與修改屬性可見度的方法一樣,即在方法聲明中添加 public、private 或 protected。如果類需要使用一些外部世界無需知道的家務(wù)管理方法,則可以將其聲明為 private。在清單 8 中,get()
方法為 Dictionary
類的用戶提供了提取翻譯的接口。該類還需要跟蹤所有查詢,因此提供了 private 方法 logQuery()
。
清單 8. get()
方法為 Dictionary
類的用戶提供了接口function get( $term ) { $value = $this->translations[$term]; $this->logQuery( $term, $value, "get" ); return $value;}private function logQuery( $term, $value, $kind ) { // write log information}
|
將 logQuery()
聲明為 private 簡(jiǎn)化了公共接口,而且防止了類不適當(dāng)?shù)卣{(diào)用 logQuery()
。與屬性一樣,嘗試從包含類外部調(diào)用私有方法將導(dǎo)致致命錯(cuò)誤。
在類上下文操作
到目前為止,所看到的方法和屬性都在對(duì)象上下文中進(jìn)行操作。也就是說,必須使用對(duì)象實(shí)例,通過 $this
偽變量或標(biāo)準(zhǔn)變量中存儲(chǔ)的對(duì)象引用來訪問方法和屬性。有時(shí)候,可能發(fā)現(xiàn)通過類而不是對(duì)象實(shí)例來訪問屬性和方法更有用。這種類成員叫做靜態(tài) 成員。
要聲明靜態(tài)屬性,將關(guān)鍵字 static
放在可見度修飾符后面,直接位于屬性變量前面。
下例顯示單個(gè)靜態(tài)屬性:$iodir
,存放用于保存和讀取 Dictionary
數(shù)據(jù)的默認(rèn)目錄的路徑。因?yàn)樵摂?shù)據(jù)對(duì)于所有對(duì)象是相同的,所以讓它可用于所有實(shí)例是有意義的。
清單 9. 單個(gè)靜態(tài) $iodir
屬性class Dictionary { public static $iodir="."; // ...}
|
可以使用范圍解析操作符來訪問靜態(tài)屬性,該操作符由雙冒號(hào) (::) 組成。范圍解析操作符應(yīng)位于類名和希望訪問的靜態(tài)屬性之間。
print Dictionary::$iodir . "\n";Dictionary::$iodir = "/tmp";
|
正如所看到的,訪問該屬性無需實(shí)例化 Dictionary
對(duì)象。
聲明和訪問靜態(tài)方法的語法與此相似。再次,應(yīng)將 static 關(guān)鍵字放在可見度修飾符后。清單 10 顯示了兩個(gè)靜態(tài)方法,它們?cè)L問聲明為 private 的 $iodir
屬性。
清單 10. 訪問 $iodir
屬性的兩個(gè)靜態(tài)方法class Dictionary { private static $iodir="."; // ... public static function setSaveDirectory( $dir ) { if ( ! is_dir( $dir ) ! is_writable( $dir ) ) { return false; } self::$iodir = $dir; } public static function getSaveDirectory( ) { return self::$iodir; } // ...}
|
用戶不再能訪問 $iodir
屬性目錄了。通過創(chuàng)建特殊方法來訪問屬性,可以確保所提供的任何值是健全的。在本例中,方法在進(jìn)行分配前檢查給定字符串指向可寫入的目錄。
注意,兩個(gè)方法都使用關(guān)鍵字 self
和訪問解析操作符來引用 $iodir
屬性。不能在靜態(tài)方法中使用 $this
,因?yàn)?$this
是對(duì)當(dāng)前對(duì)象實(shí)例的引用,但靜態(tài)方法是通過類而不是通過對(duì)象調(diào)用的。如果 PHP 引擎在靜態(tài)方法中看到 $this
,它將拋出致命錯(cuò)誤和一條提示消息。
要從類外部調(diào)用靜態(tài)方法,可使用類名加上范圍解析符和方法名。
Dictionary::setSaveDirectory("/tmp");print Dictionary::getSaveDirectory();
|
需要使用靜態(tài)方法有兩個(gè)重要原因。首先,實(shí)用程序操作可能不需要對(duì)象實(shí)例來做它的工作。通過聲明為靜態(tài),為客戶機(jī)代碼節(jié)省了創(chuàng)建對(duì)象的工作量。第二,靜態(tài)方法是全局可用的。這意味著可以設(shè)置一個(gè)所有對(duì)象實(shí)例都可以訪問的值,而且使得靜態(tài)方法成為共享系統(tǒng)上關(guān)鍵數(shù)據(jù)的好辦法。
盡管靜態(tài)屬性通常被聲明為 private 來防止別人干預(yù),但有一種方法可以創(chuàng)建只讀靜態(tài)范圍的屬性,即聲明常量。與全局屬性一樣,類常量一旦定義就不可更改。它用于狀態(tài)標(biāo)志和進(jìn)程生命周期中不發(fā)生更改的其他東西,比如 pi 或非洲的所有國(guó)家。
用 const
關(guān)鍵字聲明類常量。例如,因?yàn)?Dictionary
對(duì)象的實(shí)際實(shí)現(xiàn)背后幾乎肯定有一個(gè)數(shù)據(jù)庫,所以還可以假設(shè)項(xiàng)和翻譯有最大長(zhǎng)度。清單 11 將其設(shè)置為類常量。
清單 11. 將 MAXLENGTH
設(shè)置為類常量class Dictionary { const MAXLENGTH = 250; // ... }print Dictionary::MAXLENGTH;
|
類常量始終為 public,所以不能使用可見度關(guān)鍵字。這并是問題,因?yàn)槿魏胃钠渲档膰L試都將導(dǎo)致解析錯(cuò)誤。還要注意,與常規(guī)屬性不同,類常量不以美元符號(hào)開始。
繼承
如果熟悉面向?qū)ο缶幊,您將知道我一直把最好的留到最后。類及其生成的?dòng)態(tài)對(duì)象之間的關(guān)系使得系統(tǒng)更靈活。例如,每個(gè) Dictionary
對(duì)象封裝不同的翻譯數(shù)據(jù)集合,但是這些不同實(shí)體的模型定義在單個(gè) Dictionary
類中。
但有時(shí)候需要記下類級(jí)別的差異。是否記得 DictionaryIO
類?扼要重述一下,它從 Dictionary
對(duì)象中獲取數(shù)據(jù),將其寫入文件系統(tǒng),從一個(gè)文件中獲取數(shù)據(jù),將其合并回到 Dictionary
對(duì)象中。清單 12 顯示使用序列化來保存和加載 Dictionary
數(shù)據(jù)的快速實(shí)現(xiàn)。
清單 12. 使用序列化的快速實(shí)現(xiàn)class Dictionary { // ... function asArray() { return $this->translations; } function getType() { return $this->type; } function export() { $this->dictio->export( $this ); } function import() { $this->dictio->import( $this ); }}class DictionaryIO { function path( Dictionary $dictionary, $ext ) { $path = Dictionary::getSaveDirectory(); $path .= DIRECTORY_SEPARATOR; $path .= $dictionary->getType().".$ext"; return $path; } function export( Dictionary $dictionary ) { $translations = $dictionary->asArray(); file_put_contents( $this->path( $dictionary, 'serial'), serialize( $translations ) ); } function import( Dictionary $dictionary ) { $path = $this->path( $dictionary, 'serial' ); if ( ! is_file( $path ) ) return false; $translations = unserialize( file_get_contents( $path ) ); foreach ( $translations as $term => $trans ) { $dictionary->set( $term, $trans ); } }}$dict = new Dictionary( "En", new DictionaryIO() );$dict->set( "TREE", "tree" );$dict->export();
|
本例引入兩個(gè)簡(jiǎn)單的 Dictionary
方法,具體來說,asArray()
返回 $translations
數(shù)組的副本。DictionaryIO
實(shí)現(xiàn)具有簡(jiǎn)約的優(yōu)點(diǎn)。因?yàn)樵谑纠a中通常省略了錯(cuò)誤檢查,即便如此,這仍是將數(shù)據(jù)保存到文件中的快速簡(jiǎn)單的方法。
一旦部署了這種庫之后,則需要立即支持它的保存格式。讓格式過時(shí)會(huì)冒犯那些可能以這種方式存儲(chǔ)備份的用戶的愿望。但要求改變了,而且還可能收到輸出格式不方便用戶編輯的抱怨。這些用戶希望將導(dǎo)出文件以 XML 格式發(fā)送給第三方。
現(xiàn)在面臨一個(gè)問題。如何在 DictionaryIO
接口中支持兩種格式?
一個(gè)解決方案是在 export()
和 import()
方法中使用條件語句,測(cè)試類型標(biāo)志,如清單 13 所示。
清單 13. 在 export()
和 import()
方法中使用條件語句function export( Dictionary $dictionary ) { if ( $this->type == DictionaryIO::SERIAL ) { // write serialized data } else if ( $this->type == DictionaryIO::XML ) { // write xml data }}function import( Dictionary $dictionary ) { if ( $this->type == DictionaryIO::SERIAL ) { // read serialized data } else if ( $this->type == DictionaryIO::XML ) { // read xml data }}
|
這種結(jié)構(gòu)是壞“代碼味道”的一個(gè)例子,原因在于它依賴于復(fù)制。在一個(gè)地方進(jìn)行更改(比如,添加新類型測(cè)試)需要在其他地方進(jìn)行一組相應(yīng)的更改(將其他類型測(cè)試帶入行中),代碼很快就會(huì)變得易錯(cuò)難讀。
繼承提供了更優(yōu)雅的解決方案?梢詣(chuàng)建一個(gè)新類 XmlDictionaryIO
,該類繼承由 DictionaryIO
設(shè)置的接口,但覆蓋其中一些功能。
使用 extends 關(guān)鍵字創(chuàng)建子類。如下是 XmlDictionaryIO
類的最小實(shí)現(xiàn):
XmlDictionaryIO extends DictionaryIO {}
|
XmlDictionaryIO
現(xiàn)在的功能與 DictionaryIO
完全相同。因?yàn)樗鼜?DictionaryIO
繼承了所有的公共(和保護(hù))屬性,所以可以將應(yīng)用于 DictionaryIO
對(duì)象的相同操作應(yīng)用于 XmlDictionaryIO
對(duì)象。這種關(guān)系擴(kuò)展到對(duì)象類型。XmlDictionaryIO
對(duì)象顯然是 XmlDictionaryIO
類的實(shí)例,但它也是 DictionaryIO
的實(shí)例 —— 同樣地,以一般化的順序,一個(gè)人同時(shí)是人類、哺乳動(dòng)物和動(dòng)物?梢允褂 instanceof
操作符來測(cè)試這一點(diǎn),如果對(duì)象是指定類的成員,則返回 true,如清單 14 所示。
清單 14. 使用 instanceof
操作符測(cè)試?yán)^承$dictio = new XmlDictionaryIO();if ( $dictio instanceof XmlDictionaryIO ) { print "object is an instance of XmlDictionaryIO\n";}if ( $dictio instanceof DictionaryIO ) { print "object is an instance of DictionaryIO\n";}
|
輸出如下:
object is an instance of XmlDictionaryIOobject is an instance of DictionaryIO
|
正如 instanceof
接受 $dictio
是 DictionaryIO
對(duì)象,所以方法也將接受這些對(duì)象作為參數(shù)。這意味著 XmlDictionaryIO
對(duì)象可以被傳遞給 Dictionary
類的構(gòu)造函數(shù),即使 DictionaryIO
是由構(gòu)造函數(shù)的簽名指定的類型。
清單 15 是快而臟的 XmlDictionaryIO
實(shí)現(xiàn),使用 DOM 來完成 XML 功能。
清單 15. XmlDictionaryIO
實(shí)現(xiàn)class XmlDictionaryIO extends DictionaryIO { function export( Dictionary $dictionary ) { $translations = $dictionary->asArray(); $doc = new DOMDocument("1.0"); $dic_el = $doc->createElement( "dictionary" ); $doc->appendChild( $dic_el ); foreach ( $translations as $key => $val ) { $term_el = $doc->createElement( "term" ); $dic_el->appendChild( $term_el ); $key_el = $doc->createElement("key", $key ); $val_el = $doc->createElement( "value", $val ); $term_el->appendChild( $key_el ); $term_el->appendChild( $val_el ); } file_put_contents( $this->path( $dictionary, 'xml'), $doc->saveXML() ); } function import( Dictionary $dictionary ) { $path = $this->path( $dictionary, 'xml'); if ( ! is_file( $path ) ) return false; $doc = DOMDocument::loadXML( file_get_contents( $path ) ); $termlist = $doc ->getElementsByTagName( "term" ); foreach ( $termlist as $term ) { $key = $term->getElementsByTagName( "key" ) ->item( 0 )->nodeValue; $val = $term ->getElementsByTagName( "value" ) ->item( 0 )->nodeValue; $dictionary->set( $key, $val ); } }}
|
有關(guān)獲得并生成 XML 的詳細(xì)信息是當(dāng)然要介紹的。有許多方法能完成這一操作,其中包括完美的 SimpleXML 擴(kuò)展。簡(jiǎn)言之,import()
方法以 XML 文檔為參數(shù),并使用它來填充 Dictionary
對(duì)象。export()
方法從 Dictionary
對(duì)象中取得數(shù)據(jù),并將其寫入 XML 文件中。(在現(xiàn)實(shí)世界中,可能會(huì)使用叫做 XLIFF 的基于 XML 的格式,該格式適用于導(dǎo)入到第三方翻譯工具中。)
注意,import()
和 export()
都調(diào)用實(shí)用程序方法 path()
,該方法不存在于 XmlDictionaryIO
類中。但沒有關(guān)系,因?yàn)?path()
在 DictionaryIO
中實(shí)現(xiàn)。當(dāng) XmlDictionaryIO
實(shí)現(xiàn)一個(gè)方法時(shí),則當(dāng)調(diào)用該方法時(shí),會(huì)為 XmlDictionaryIO
對(duì)象調(diào)用該實(shí)現(xiàn)。當(dāng)沒有任何實(shí)現(xiàn)存在時(shí),調(diào)用失敗返回給父類。
圖 2 顯示了 DictionaryIO
和 XmlDictionaryIO
類之間的繼承關(guān)系。封閉的箭頭表示繼承,從子類指向父類。
圖 2. 繼承關(guān)系
結(jié)束語
由于篇幅有限,因此不可能全部介紹。進(jìn)一步研究有兩個(gè)方向:廣度和深度。廣度指的是超出本文范圍的那些特性,比如抽象類、接口、迭代器接口、反射、異常和對(duì)象復(fù)制。深度指的是設(shè)計(jì)問題。盡管理解 PHP 中可用于面向?qū)ο缶幊痰墓ぞ叻秶苤匾,但考慮如何最佳使用這些特性同樣重要。幸運(yùn)的是,專門講述面向?qū)ο笊舷挛闹性O(shè)計(jì)模式的可用參考資料很多(參閱“參考資料”)
如果仍在使用 PHP V4 編程,我希望您查找足夠新的特性來證明遷移到 V5 及其面向?qū)ο蠛诵奶匦允钦_的。不久您就會(huì)驚訝地發(fā)現(xiàn)自己無法離開它們了。