隨筆-88  評(píng)論-77  文章-48  trackbacks-0
          PHP V5 新的面向?qū)ο缶幊烫匦燥@著提升了這個(gè)流行語(yǔ)言中的功能層次。學(xué)習(xí)如何用 PHP V5 動(dòng)態(tài)特性創(chuàng)建可以滿足需求的對(duì)象。

          PHP V5 中新的面向?qū)ο缶幊蹋∣OP)特性的引入顯著提升了這個(gè)編程語(yǔ)言的功能層次。現(xiàn)在不僅有了私有的、受保護(hù)的和公共的成員變量和函數(shù) —— 就像在 Java?、 C++ 或 C# 編程語(yǔ)言中一樣 —— 但是還可以創(chuàng)建在運(yùn)行時(shí)變化的對(duì)象,即動(dòng)態(tài)地創(chuàng)建新方法和成員變量。而使用 Java、C++ 或 C# 語(yǔ)言是做不到這件事的。這種功能使得超級(jí)快速的應(yīng)用程序開發(fā)系統(tǒng)(例如 Ruby on Rails)成為可能。

          但是,在進(jìn)入這些之前,有一點(diǎn)要注意:本文介紹 PHP V5 中非常高級(jí)的 OOP 特性的使用,但是這類特性不是在每個(gè)應(yīng)用程序中都需要的。而且,如果不具備 OOP 的堅(jiān)實(shí)基礎(chǔ)以及 PHP 對(duì)象語(yǔ)法的初步知識(shí),這類特性將會(huì)很難理解。

          動(dòng)態(tài)的重要性

          對(duì)象是把雙刃劍。一方面,對(duì)象是封裝數(shù)據(jù)和邏輯并創(chuàng)建更容易維護(hù)的系統(tǒng)的重大方式。但另一方面,它們會(huì)變得很繁瑣,需要許多冗余的代碼,這時(shí)可能最希望做到的就是不要犯錯(cuò)。這類問題的一個(gè)示例來(lái)自數(shù)據(jù)庫(kù)訪問對(duì)象。一般來(lái)說,想用一個(gè)類代表每個(gè)數(shù)據(jù)庫(kù)表,并執(zhí)行以下功能:對(duì)象從數(shù)據(jù)庫(kù)讀出數(shù)據(jù)行;允許更新字段,然后用新數(shù)據(jù)更新數(shù)據(jù)庫(kù)或刪除行。還有一種方法可以創(chuàng)建新的空對(duì)象,設(shè)置對(duì)象的字段,并把數(shù)據(jù)插入數(shù)據(jù)庫(kù)。

          如果在數(shù)據(jù)庫(kù)中有一個(gè)表,名為 Customers,那么就應(yīng)當(dāng)有一個(gè)對(duì)象,名為 Customer,它應(yīng)當(dāng)擁有來(lái)自表的字段,并代表一個(gè)客戶。而且 Customer 對(duì)象應(yīng)當(dāng)允許插入、更新或刪除數(shù)據(jù)庫(kù)中對(duì)應(yīng)的記錄。現(xiàn)在,一切都很好,而且有也很多意義。但是,有許多代碼要編寫。如果在數(shù)據(jù)庫(kù)中有 20 個(gè)表,就需要 20 個(gè)類。

          有三個(gè)解決方案可以采用。第一個(gè)解決方案就是,坐在鍵盤前,老老實(shí)實(shí)地錄入一段時(shí)間。對(duì)于小項(xiàng)目來(lái)說,這還可以,但是我很懶。第二個(gè)解決方案是用代碼生成器,讀取數(shù)據(jù)庫(kù)模式,并自動(dòng)編寫代碼。這是個(gè)好主意,而且是另一篇文章的主題。第三個(gè)解決方案,也是我在本文中介紹的,是編寫一個(gè)類,在運(yùn)行時(shí)動(dòng)態(tài)地把自己塑造成指定表的字段。這個(gè)類執(zhí)行起來(lái)比起特定于表的類可能有點(diǎn)慢 —— 但是把我從編寫大量代碼中解脫出來(lái)。這個(gè)解決方案在項(xiàng)目開始的時(shí)候特別有用,因?yàn)檫@時(shí)表和字段不斷地變化,所以跟上迅速的變化是至關(guān)重要的。

          所以,如何才能編寫一個(gè)能夠彎曲 的類呢?





          回頁(yè)首


          寫一個(gè)柔性的類

          對(duì)象有兩個(gè)方面:成員變量方法。在編譯語(yǔ)言(例如 Java)中,如果想調(diào)用不存在的方法或引用不存在的成員變量,會(huì)得到編譯時(shí)錯(cuò)誤。但是,在非編譯語(yǔ)言,例如 PHP 中,會(huì)發(fā)生什么?

          在 PHP 中的方法調(diào)用是這樣工作的。首先,PHP 解釋器在類上查找方法。如果方法存在,PHP 就調(diào)用它。如果沒有,那么就調(diào)用類上的魔法方法 __call(如果這個(gè)方法存在的話)。如果 __call 失敗,就調(diào)用父類方法,依此類推。

          魔法方法
          魔法方法是有特定名稱的方法,PHP 解釋器在腳本執(zhí)行的特定點(diǎn)上會(huì)查找魔法方法。最常見的魔法方法就是對(duì)象創(chuàng)始時(shí)調(diào)用的構(gòu)造函數(shù)。

          __call 方法有兩個(gè)參數(shù):被請(qǐng)求的方法的名稱和方法參數(shù)。如果創(chuàng)建的 __call 方法接受這兩個(gè)參數(shù),執(zhí)行某項(xiàng)功能,然后返回 TRUE,那么調(diào)用這個(gè)對(duì)象的代碼就永遠(yuǎn)不會(huì)知道在有代碼的方法和 __call 機(jī)制處理的方法之間的區(qū)別。通過這種方式,可以創(chuàng)建這樣的對(duì)象,即動(dòng)態(tài)地模擬擁有無(wú)數(shù)方法的情況。

          除了 __call 方法,其他魔法方法 —— 包括 __get__set —— 調(diào)用它們的時(shí)候,都是因?yàn)橐昧瞬淮嬖诘膶?shí)例變量。腦子里有了這個(gè)概念之后,就可以開始編寫能夠適應(yīng)任何表的動(dòng)態(tài)數(shù)據(jù)庫(kù)訪問類了。





          回頁(yè)首


          經(jīng)典的數(shù)據(jù)庫(kù)訪問

          先從一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)模式開始。清單 1 所示的模式針對(duì)的是單一的數(shù)據(jù)表數(shù)據(jù)庫(kù),容納圖書列表。


          清單 1. MySQL 數(shù)據(jù)庫(kù)模式
          														
          																DROP TABLE IF EXISTS book;
          CREATE TABLE book (
                  book_id INT NOT NULL AUTO_INCREMENT,
                  title TEXT,
                  publisher TEXT,
                  author TEXT,
                  PRIMARY KEY( book_id )
          );
          
          														
          												

          請(qǐng)把這個(gè)模式裝入到名為 bookdb 的數(shù)據(jù)庫(kù)。

          接下來(lái),編寫一個(gè)常規(guī)的數(shù)據(jù)庫(kù)類,然后再把它修改成動(dòng)態(tài)的。清單 2 顯示了圖書表的簡(jiǎn)單的數(shù)據(jù)庫(kù)訪問類。


          清單 2. 基本的數(shù)據(jù)庫(kù)訪問客戶機(jī)
          														
          																<?php
          require_once("DB.php");
          
          $dsn = 'mysql://root:password@localhost/bookdb';
          $db =& DB::Connect( $dsn, array() );
          if (PEAR::isError($db)) { die($db->getMessage()); }
          
          class Book
          {
            private $book_id;
            private $title;
            private $author;
            private $publisher;
          
            function __construct()
            {
            }
          
            function set_title( $title ) { $this->title = $title; }
            function get_title( ) { return $this->title; }
          
            function set_author( $author ) { $this->author = $author; }
            function get_author( ) { return $this->author; }
          
            function set_publisher( $publisher ) {
            $this->publisher = $publisher; }
            function get_publisher( ) { return $this->publisher; }
          
            function load( $id )
            {
              global $db;
          $res = $db->query( "SELECT * FROM book WHERE book_id=?",
              array( $id ) );
              $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
              $this->book_id = $id;
              $this->title = $row['title'];
              $this->author = $row['author'];
              $this->publisher = $row['publisher'];
            }
          
            function insert()
            {
              global $db;
              $sth = $db->prepare(
          'INSERT INTO book ( book_id, title, author, publisher )
              VALUES ( 0, ?, ?, ? )'
              );
              $db->execute( $sth,
                array( $this->title,
                  $this->author,
                  $this->publisher ) );
              $res = $db->query( "SELECT last_insert_id()" );
              $res->fetchInto( $row );
              return $row[0];
            }
          
            function update()
            {
              global $db;
              $sth = $db->prepare(
          'UPDATE book SET title=?, author=?, publisher=?
             WHERE book_id=?'
              );
              $db->execute( $sth,
                array( $this->title,
                  $this->author,
                  $this->publisher,
                  $this->book_id ) );
            }
          
            function delete()
            {
              global $db;
              $sth = $db->prepare(
                'DELETE FROM book WHERE book_id=?'
              );
              $db->execute( $sth,
                array( $this->book_id ) );
            }
          
            function delete_all()
            {
              global $db;
              $sth = $db->prepare( 'DELETE FROM book' );
              $db->execute( $sth );
            }
          }
          
          $book = new Book();
          $book->delete_all();
          $book->set_title( "PHP Hacks" );
          $book->set_author( "Jack Herrington" );
          $book->set_publisher( "O'Reilly" );
          $id = $book->insert();
          echo ( "New book id = $id\n" );
          
          $book2 = new Book();
          $book2->load( $id );
          echo( "Title = ".$book2->get_title()."\n" );
          $book2->delete( );
          ?>
          
          														
          												

          為了保持代碼簡(jiǎn)單,我把類和測(cè)試代碼放在一個(gè)文件中。文件首先得到數(shù)據(jù)庫(kù)句柄,句柄保存在一個(gè)全局變量中。然后定義 Book 類,用私有成員變量代表每個(gè)字段。還包含了一套用來(lái)從數(shù)據(jù)庫(kù)裝入、插入、更新和刪除行的方法。

          底部的測(cè)試代碼先刪除數(shù)據(jù)庫(kù)中的所有條目。然后,代碼插入一本書,輸出新記錄的 ID。然后,代碼把這本書裝入另一個(gè)對(duì)象并輸出書名。

          清單 3 顯示了在命令行上用 PHP 解釋器運(yùn)行代碼的效果。


          清單 3. 在命令行運(yùn)行代碼
          														
          																% php db1.php
          New book id = 25
          Title = PHP Hacks
          %
          
          														
          												

          不需要看太多,就已經(jīng)得到重點(diǎn)了。Book 對(duì)象代表圖書數(shù)據(jù)表中的行。通過使用上面的字段和方法,可以創(chuàng)建新行、更新行和刪除行。





          回頁(yè)首


          初識(shí)動(dòng)態(tài)

          下一步是讓類變得稍微動(dòng)態(tài)一些:動(dòng)態(tài)地為每個(gè)字段創(chuàng)建 get_set_ 方法。清單 4 顯示了更新后的代碼。


          清單 4. 動(dòng)態(tài) get_ 和 set_ 方法
          														
          																<?php
          require_once("DB.php");
          
          $dsn = 'mysql://root:password@localhost/bookdb';
          $db =& DB::Connect( $dsn, array() );
          if (PEAR::isError($db)) { die($db->getMessage()); }
          
          class Book
          {
            private $book_id;
            private $fields = array();
          
            function __construct()
            {
              $this->fields[ 'title' ] = null;
              $this->fields[ 'author' ] = null;
              $this->fields[ 'publisher' ] = null;
            }
          
            function __call( $method, $args )
            {
              if ( preg_match( "/set_(.*)/", $method, $found ) )
              {
                if ( array_key_exists( $found[1], $this->fields ) )
                {
                  $this->fields[ $found[1] ] = $args[0];
                  return true;
                }
              }
              else if ( preg_match( "/get_(.*)/", $method, $found ) )
              {
                if ( array_key_exists( $found[1], $this->fields ) )
                {
                  return $this->fields[ $found[1] ];
                }
              }
              return false;
            }
          
            function load( $id )
            {
              global $db;
          $res = $db->query( "SELECT * FROM book WHERE book_id=?",
             array( $id ) );
              $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
              $this->book_id = $id;
              $this->set_title( $row['title'] );
              $this->set_author( $row['author'] );
              $this->set_publisher( $row['publisher'] );
            }
          
            function insert()
            {
              global $db;
              $sth = $db->prepare(
          'INSERT INTO book ( book_id, title, author, publisher )
             VALUES ( 0, ?, ?, ? )'
              );
              $db->execute( $sth,
                array( $this->get_title(),
                  $this->get_author(),
                  $this->get_publisher() ) );
              $res = $db->query( "SELECT last_insert_id()" );
              $res->fetchInto( $row );
              return $row[0];
            }
          
            function update()
            {
              global $db;
              $sth = $db->prepare(
          'UPDATE book SET title=?, author=?, publisher=?
            WHERE book_id=?'
              );
              $db->execute( $sth,
                array( $this->get_title(),
                  $this->get_author(),
                  $this->get_publisher(),
                  $this->book_id ) );
            }
          
            function delete()
            {
              global $db;
              $sth = $db->prepare(
                'DELETE FROM book WHERE book_id=?'
              );
              $db->execute( $sth,
                array( $this->book_id ) );
            }
          
            function delete_all()
            {
              global $db;
              $sth = $db->prepare( 'DELETE FROM book' );
              $db->execute( $sth );
            }
          }
          
          ..
          
          														
          												

          要做這個(gè)變化,需要做兩件事。首先,必須把字段從單個(gè)實(shí)例變量修改成字段和值組合構(gòu)成的散列表。然后必須添加一個(gè) __call 方法,它只查看方法名稱,看方法是 set_ 還是 get_ 方法,然后在散列表中設(shè)置適當(dāng)?shù)淖侄巍?/font>

          注意,load 方法通過調(diào)用 set_titleset_authorset_publisher方法 —— 實(shí)際上都不存在 —— 來(lái)實(shí)際使用 __call 方法。





          回頁(yè)首


          走向完全動(dòng)態(tài)

          刪除 get_set_ 方法只是一個(gè)起點(diǎn)。要?jiǎng)?chuàng)建完全動(dòng)態(tài)的數(shù)據(jù)庫(kù)對(duì)象,必須向類提供表和字段的名稱,還不能有硬編碼的引用。清單 5 顯示了這個(gè)變化。


          清單 5. 完全動(dòng)態(tài)的數(shù)據(jù)庫(kù)對(duì)象類
          														
          																<?php
          require_once("DB.php");
          
          $dsn = 'mysql://root:password@localhost/bookdb';
          $db =& DB::Connect( $dsn, array() );
          if (PEAR::isError($db)) { die($db->getMessage()); }
          
          class DBObject
          {
            private $id = 0;
            private $table;
            private $fields = array();
          
            function __construct( $table, $fields )
            {
              $this->table = $table;
              foreach( $fields as $key )
                $this->fields[ $key ] = null;
            }
          
            function __call( $method, $args )
            {
              if ( preg_match( "/set_(.*)/", $method, $found ) )
              {
                if ( array_key_exists( $found[1], $this->fields ) )
                {
                  $this->fields[ $found[1] ] = $args[0];
                  return true;
                }
              }
              else if ( preg_match( "/get_(.*)/", $method, $found ) )
              {
                if ( array_key_exists( $found[1], $this->fields ) )
                {
                  return $this->fields[ $found[1] ];
                }
              }
              return false;
            }
          
            function load( $id )
            {
              global $db;
              $res = $db->query(
            "SELECT * FROM ".$this->table." WHERE ".
            $this->table."_id=?",
                array( $id )
              );
              $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
              $this->id = $id;
              foreach( array_keys( $row ) as $key )
                $this->fields[ $key ] = $row[ $key ];
            }
          
            function insert()
            {
              global $db;
          
              $fields = $this->table."_id, ";
              $fields .= join( ", ", array_keys( $this->fields ) );
          
              $inspoints = array( "0" );
              foreach( array_keys( $this->fields ) as $field )
                $inspoints []= "?";
              $inspt = join( ", ", $inspoints );
          
          $sql = "INSERT INTO ".$this->table." ( $fields )
             VALUES ( $inspt )";
          
              $values = array();
              foreach( array_keys( $this->fields ) as $field )
                $values []= $this->fields[ $field ];
          
              $sth = $db->prepare( $sql );
              $db->execute( $sth, $values );
          
              $res = $db->query( "SELECT last_insert_id()" );
              $res->fetchInto( $row );
              $this->id = $row[0];
              return $row[0];
            }
          
            function update()
            {
              global $db;
          
              $sets = array();
              $values = array();
              foreach( array_keys( $this->fields ) as $field )
              {
                $sets []= $field.'=?';
                $values []= $this->fields[ $field ];
              }
              $set = join( ", ", $sets );
              $values []= $this->id;
          
          $sql = 'UPDATE '.$this->table.' SET '.$set.
            ' WHERE '.$this->table.'_id=?';
          
              $sth = $db->prepare( $sql );
              $db->execute( $sth, $values );
            }
          
            function delete()
            {
              global $db;
              $sth = $db->prepare(
             'DELETE FROM '.$this->table.' WHERE '.
             $this->table.'_id=?'
              );
              $db->execute( $sth,
                array( $this->id ) );
            }
          
            function delete_all()
            {
              global $db;
              $sth = $db->prepare( 'DELETE FROM '.$this->table );
              $db->execute( $sth );
            }
          }
          
          $book = new DBObject( 'book', array( 'author',
             'title', 'publisher' ) );
          $book->delete_all();
          $book->set_title( "PHP Hacks" );
          $book->set_author( "Jack Herrington" );
          $book->set_publisher( "O'Reilly" );
          $id = $book->insert();
          
          echo ( "New book id = $id\n" );
          
          $book->set_title( "Podcasting Hacks" );
          $book->update();
          
          $book2 = new DBObject( 'book', array( 'author',
            'title', 'publisher' ) );
          $book2->load( $id );
          echo( "Title = ".$book2->get_title()."\n" );
          $book2->delete( );
          ? >
          
          														
          												

          在這里,把類的名稱從 Book 改成 DBObject。然后,把構(gòu)造函數(shù)修改成接受表的名稱和表中字段的名稱。之后,大多數(shù)變化發(fā)生在類的方法中,過去使用一些硬編碼結(jié)構(gòu)化查詢語(yǔ)言(SQL),現(xiàn)在則必須用表和字段的名稱動(dòng)態(tài)地創(chuàng)建 SQL 字符串。

          代碼的惟一假設(shè)就是只有一個(gè)主鍵字段,而且這個(gè)字段的名稱是表名加上 _id。所以,在 book 表這個(gè)示例中,有一個(gè)主鍵字段叫做 book_id。主鍵的命名標(biāo)準(zhǔn)可能不同;如果這樣,需要修改代碼以符合標(biāo)準(zhǔn)。

          這個(gè)類比最初的 Book 類復(fù)雜得多。但是,從類的客戶的角度來(lái)看,這個(gè)類用起來(lái)仍很簡(jiǎn)單。也就是說,我認(rèn)為這個(gè)類能更簡(jiǎn)單。具體來(lái)說,我不愿意每次創(chuàng)建圖書的時(shí)候都要指定表和字段的名稱。如果我四處拷貝和粘貼這個(gè)代碼,然后修改了 book 表的字段結(jié)構(gòu),那么我可能就麻煩了。在清單 6 中,通過創(chuàng)建一個(gè)繼承自 DBObject 的簡(jiǎn)單 Book 類,我解決了這個(gè)問題。


          清單 6. 新的 Book 類
          														
          																..
          class Book extends DBObject 
          {
            function __construct()
            {
              parent::__construct( 'book', 
                array( 'author', 'title', 'publisher' ) );
            }
          }
          
          $book = new Book( );
          $book->delete_all();
          $book->{'title'} = "PHP Hacks";
          $book->{'author'} = "Jack Herrington";
          $book->{'publisher'} = "O'Reilly";
          $id = $book->insert();
          
          echo ( "New book id = $id\n" );
          
          $book->{'title'} = "Podcasting Hacks";
          $book->update();
          
          $book2 = new Book( );
          $book2->load( $id );
          echo( "Title = ".$book2->{'title'}."\n" );
          $book2->delete( );
          ?>
          
          														
          												

          現(xiàn)在,Book 類真的是簡(jiǎn)單了。而且 Book 類的客戶也不再需要知道表或字段的名稱了。





          回頁(yè)首


          改進(jìn)的空間

          對(duì)這個(gè)動(dòng)態(tài)類我想做的最后一個(gè)改進(jìn),是用成員變量訪問字段,而不是用笨重的 get_set_ 操作符。清單 7 顯示了如何用 __get__set 魔法方法代替 __call


          清單 7. 使用 __get 和 __set 方法
          														
          																<?php
          require_once("DB.php");
          
          $dsn = 'mysql://root:password@localhost/bookdb';
          $db =& DB::Connect( $dsn, array() );
          if (PEAR::isError($db)) { die($db->getMessage()); }
          
          class DBObject
          {
            private $id = 0;
            private $table;
            private $fields = array();
          
            function __construct( $table, $fields )
            {
              $this->table = $table;
              foreach( $fields as $key )
                $this->fields[ $key ] = null;
            }
          
            function __get( $key )
            {
              return $this->fields[ $key ];
            }
          
            function __set( $key, $value )
            {
              if ( array_key_exists( $key, $this->fields ) )
              {
                $this->fields[ $key ] = $value;
                return true;
              }
              return false;
            }
          
            function load( $id )
            {
              global $db;
              $res = $db->query(
            "SELECT * FROM ".$this->table." WHERE ".
             $this->table."_id=?",
                array( $id )
              );
              $res->fetchInto( $row, DB_FETCHMODE_ASSOC );
              $this->id = $id;
              foreach( array_keys( $row ) as $key )
                $this->fields[ $key ] = $row[ $key ];
            }
          
            function insert()
            {
              global $db;
          
              $fields = $this->table."_id, ";
              $fields .= join( ", ", array_keys( $this->fields ) );
          
              $inspoints = array( "0" );
              foreach( array_keys( $this->fields ) as $field )
                $inspoints []= "?";
              $inspt = join( ", ", $inspoints );
          
          $sql = "INSERT INTO ".$this->table. 
             " ( $fields ) VALUES ( $inspt )";
          
              $values = array();
              foreach( array_keys( $this->fields ) as $field )
                $values []= $this->fields[ $field ];
          
              $sth = $db->prepare( $sql );
              $db->execute( $sth, $values );
          
              $res = $db->query( "SELECT last_insert_id()" );
              $res->fetchInto( $row );
              $this->id = $row[0];
              return $row[0];
            }
          
            function update()
            {
              global $db;
          
              $sets = array();
              $values = array();
              foreach( array_keys( $this->fields ) as $field )
              {
                $sets []= $field.'=?';
                $values []= $this->fields[ $field ];
              }
              $set = join( ", ", $sets );
              $values []= $this->id;
          
          $sql = 'UPDATE '.$this->table.' SET '.$set.
            ' WHERE '.$this->table.'_id=?';
          
              $sth = $db->prepare( $sql );
              $db->execute( $sth, $values );
            }
          
            function delete()
            {
              global $db;
              $sth = $db->prepare(
          'DELETE FROM '.$this->table.' WHERE '.
          $this->table.'_id=?'
              );
              $db->execute( $sth,
                array( $this->id ) );
            }
          
            function delete_all()
            {
              global $db;
              $sth = $db->prepare( 'DELETE FROM '.$this->table );
              $db->execute( $sth );
            }
          }
          
          class Book extends DBObject 
          {
            function __construct()
            {
            parent::__construct( 'book',
              array( 'author', 'title', 'publisher' ) );
            }
          }
          
          $book = new Book( );
          $book->delete_all();
          $book->{'title'} = "PHP Hacks";
          $book->{'author'} = "Jack Herrington";
          $book->{'publisher'} = "O'Reilly";
          $id = $book->insert();
          
          echo ( "New book id = $id\n" );
          
          $book->{'title'} = "Podcasting Hacks";
          $book->update();
          
          $book2 = new Book( );
          $book2->load( $id );
          echo( "Title = ".$book2->{'title'}."\n" );
          $book2->delete( );
          ?>
          
          														
          												

          底部的測(cè)試代碼只演示了這個(gè)語(yǔ)法干凈了多少。要得到圖書的書名,只需得到 title 成員變量。這個(gè)變量會(huì)調(diào)用對(duì)象的 __get 方法,在散列表中查找 title 條目并返回。

          現(xiàn)在就得到了單個(gè)動(dòng)態(tài)的數(shù)據(jù)庫(kù)訪問類,它能夠讓自己適應(yīng)到數(shù)據(jù)庫(kù)中的任何表。





          回頁(yè)首


          動(dòng)態(tài)類的更多用途

          編寫動(dòng)態(tài)類不僅限于數(shù)據(jù)庫(kù)訪問。請(qǐng)看清單 8 中的 Customer 對(duì)象這個(gè)例子。


          清單 8. 簡(jiǎn)單的 Customer 對(duì)象
          														
          																<?php
          class Customer
          {
            private $name;
          
            function set_name( $value )
            {
              $this->name = $value;
            }
          
            function get_name()
            {
              return $this->name;
            }
          }
          
          $c1 = new Customer();
          $c1->set_name( "Jack" );
          $name = $c1->get_name();
          echo( "name = $name\n" );
          ?>
          
          														
          												

          這個(gè)對(duì)象足夠簡(jiǎn)單。但是如果我想在每次檢索或設(shè)置客戶名稱時(shí)都記錄日志,會(huì)發(fā)生什么呢?我可以把這個(gè)對(duì)象包裝在一個(gè)動(dòng)態(tài)日志對(duì)象內(nèi),這個(gè)對(duì)象看起來(lái)像 Customer 對(duì)象,但是會(huì)把 getset 操作的通知發(fā)送給日志。清單 9 顯示了這類包裝器對(duì)象。


          清單 9. 動(dòng)態(tài)包裝器對(duì)象
          														
          																<?php
          class Customer
          {
            private $name;
          
            function set_name( $value )
            {
              $this->name = $value;
            }
          
            function get_name()
            {
              return $this->name;
            }
          }
          
          class Logged
          {
            private $obj;
          
            function __call( $method, $args )
            {
              echo( "$method( ".join( ",", $args )." )\n" );
          return call_user_func_array(array(&$this->obj,
             $method), $args );
            }
          
            function __construct( $obj )
            {
              $this->obj = $obj;
            }
          }
          
          $c1 = new Logged( new Customer() );
          $c1->set_name( "Jack" );
          $name = $c1->get_name();
          echo( "name = $name\n" );
          ?>
          
          														
          												

          調(diào)用日志版本的 Customer 的代碼看起來(lái)與前面相同,但是這時(shí),對(duì) Customer 對(duì)象的任何訪問都被記入日志。清單 10 顯示了運(yùn)行這個(gè)日志版代碼時(shí)輸出的日志。


          清單 10. 運(yùn)行日志版對(duì)象
          														
          																% php log2.php
          set_name( Jack )
          get_name(  )
          name = Jack
          %
          
          														
          												

          在這里,日志輸出表明用參數(shù) Jack 調(diào)用了set_name 方法。然后,調(diào)用 get_name 方法。最后,測(cè)試代碼輸出 get_name 調(diào)用的結(jié)果。





          回頁(yè)首


          結(jié)束語(yǔ)

          如果這個(gè)動(dòng)態(tài)對(duì)象素材對(duì)您來(lái)說理解起來(lái)有點(diǎn)難,我不會(huì)責(zé)備您。因?yàn)槲易约阂不瞬簧贂r(shí)間研究它并使用代碼才理解它并看出它的好處。

          動(dòng)態(tài)對(duì)象有許多功能,但是也有相當(dāng)?shù)娘L(fēng)險(xiǎn)。首先,在剛開始編寫魔法方法時(shí),類的復(fù)雜性顯著增加。這些類更難理解、調(diào)試和維護(hù)。另外,因?yàn)榧砷_發(fā)環(huán)境(IDE)變得越來(lái)越智能,所以在處理動(dòng)態(tài)類時(shí)它們也會(huì)遇到這類問題,因?yàn)楫?dāng)它們?cè)陬惿喜檎曳椒〞r(shí)會(huì)找不到方法。

          現(xiàn)在,并不是說應(yīng)當(dāng)避免編寫這類代碼。相反。我非常喜歡 PHP 的設(shè)計(jì)者這么有想法,把這些魔法方法包含在語(yǔ)言中,這樣我們才能編寫這類代碼。但是重要的是,既要理解優(yōu)點(diǎn),也要理解不足。

          當(dāng)然,對(duì)于應(yīng)用程序(例如數(shù)據(jù)庫(kù)訪問)來(lái)說,在這里介紹的技術(shù) —— 與廣泛流行的 Ruby on Rails 系統(tǒng)上使用的技術(shù)類似 —— 能夠極大地減少用 PHP 實(shí)現(xiàn)數(shù)據(jù)庫(kù)應(yīng)用程序所需要的時(shí)間。節(jié)約時(shí)間總不是壞事。





          回頁(yè)首


          參考資料

          學(xué)習(xí)
          posted on 2006-04-28 16:02 崛起的程序員 閱讀(214) 評(píng)論(0)  編輯  收藏 所屬分類: 載選文章
          主站蜘蛛池模板: 米脂县| 建宁县| 高淳县| 长乐市| 柳江县| 枣强县| 南澳县| 措美县| 西盟| 泰和县| 高安市| 灵山县| 安西县| 陇西县| 田阳县| 来凤县| 诏安县| 嵊州市| 喀喇沁旗| 阿克陶县| 丘北县| 来凤县| 乾安县| 杭锦后旗| 保定市| 利津县| 伊吾县| 寿宁县| 凯里市| 池州市| 广元市| 清水河县| 阆中市| 中西区| 兴化市| 建德市| 铁力市| 南华县| 大关县| 白朗县| 夏河县|