為-面向-對像-而生的PHP5

Published on 06/20,2004

轉載自 http://www.twbb.org/forum/?board=80;action=display;threadid=2919




原文URL: http://www.atmarkit.co.jp/flinux/special/php5/php5a.html
翻譯:Victoria / 孤獨過客

如需轉載,請指明出處
---------------------------------------------



[摘要]目前開發中的PHP5,其面向對象的機能已經被大幅度的強化了。下一代的PHP將會是怎樣的一種語言呢?下面我們來詳細講解一下目前發佈的PHP5的beta release。




(一) Zend 2.0的誕生
現在的PHP4所使用的基本文法是被稱之為Zend 引擎的腳本編譯引擎。這個就是PHP4的優良機能的原因之一,是作為對PHP3的改進而生成的一種語言。大家一直認為,PHP4的性能根據當初的目標,比PHP3有了很大的提升,在網絡編程的世界裡佔據了很大的份額。

開 發了Zend 引擎的Zend公司是在開發PHP4的同時,由PHP3的主要開發者Zeev Suraski和Andi Gutmans所創立的企業合併而來的。Zend的名稱是由Zeev和Andi的名字合起來組成的。Zend公司的商業模式是,持續不斷的為open source提供zend 引擎的PHP內核 (core),同時提升周邊產品開發和販賣的利益。以open source software作為基盤的商業,在世界範圍內大多數正在苦戰的企業中,算是比較好的典型例子了。

■PHP4的局限

托PHP4 成功的福,這個用途的適用範圍逐漸變廣起來。作為企業級的用途而使用PHP的說法時有所聞。因此,就有了這樣一個問題,構築大規模網站的時候,代碼的再利 用性十分差。具體來說就是,PHP4的面向對像性能很弱,因此習慣於使用Java等的技術人員對此有很多的抱怨。

逐步的改善PHP4的面向對象的性能,大幅度的更改基本文法,開發者達成了更新PHP記述方法的開拓目的。

■Zend 2.0開始開發
隨 後,Zend公司PHP中心的開發者們在2001年7月發表了作為下一代PHP語言引擎的Zend 2.0引擎的構想。以[Zend Engine version 2.0: Feature Overview and Design](http://www.zend.com/engine2/ZendEngine-2.0.pdf)作為目標的同時,面向對象的性能大幅 度的強化了。

目前的PHP4 Zend 引擎的擴張情況與昔日的PHP3如出一轍。這就意味著,要提升新的語言引擎的主版本號,明確方法目標,迎接來自開發團體的稱讚。

Ze2的開發,與以往的Zend引擎一樣,都是運行在open source的模式下的。最新的源代碼在CVS上被全面的公開,因為是面向開放的開發者的,關於開發的議論非常的活躍。

現在Ze2被決定採用於PHP的下一個版本PHP5中。最終發佈的時間現在還未定,但是假如根據Zend公司2003年4月1日發佈的Newsletter的話,現在的應該就是Beta Release了。

摘要]目前開發中的PHP5,其面向對象的機能已經被大幅度的強化了。下一代的PHP將會是怎樣的一種語言呢?下面我們來詳細講解一下目前發佈的PHP5的beta release。




(二) PHP5的新特性

接下來請按照順序看一下被強化的PHP5的性能。首先是最為重要的面向對像性能,類的實體特性在大幅度的被修改著。這裡說的僅是關於類的新特性。

· 對象的參照過渡是默認的(default)
· 引入訪問屬性的限制
· 引入訪問方法的限制
· 抽像類和抽像方法
· 接口
· final聲明
· 名空間
· 類內常量
· 類變量
· 統一構建器
· 析構函數(Distructor)
· 其他附屬特性

以上內容是根據2003年4月22日CVS上登錄版本資料所寫的,在正式的發佈之前,也有變動的可能性。

■對象的默認參照過渡

在PHP4 中,在以變量$var1為類的實體對象的時候,如果$var2 = $var1;那麼,在$var2中,$var1的複製被代入。明顯的,$var2為了指向與$var1相同的對象,就要寫成$var2 =& $var1,必須要加上&作為參照。
而在PHP5,對象的代入將成為自動的參照過渡。也就是說,$var2=$var1,兩者指向相同的對象。如果想要同php4一樣,帶入copy,那麼就會運用到導入__clone()的方法。
$var2 = $var1->__clone();此處,clone前面是兩個連續的「_」
(這僅僅是類的實體的特性)

■引入訪問屬性的限制

在PHP4的類中,連同屬性和方法在內,可以自由的訪問類的內外任何地方,而沒有限制。因此,用戶就無法防範屬性的無意中的更改。

而在PHP5中,同C++和Java一樣,導入了private, protected, public三個等級的訪問限制,使得類的設計者能夠對屬性和方法的使用方法進行限定。以下是各種訪問限制的意思。

· Public: 可以自由的在類的內外任何地方進行參照、變更
· Private: 只能在這個類的方法中進行參照、變更
· Protected:能夠在這個類以及繼承了這個類的另一個類的方法中進行參照、變更。另外,在繼承的類中,能夠寫入訪問指定。

在PHP4中的「var」,同以往一樣與public有著相同的意思。下面就來舉一個例子,讓我們來看看訪問限制是怎樣起作用的。

class Hoge1 {
 private $var1 = 'A';
 protected $var2 = 'B';
 protected $var3 = 'C';

 function setLower() {
  $this->var1 = 'a';
  $this->var2 = 'b';
  $this->var3 = 'c';
 }
 function var1() {
  return $this->var1;
 }
 function var2() {
  return $this->var2;
 }
 function var3() {
  return $this->var3;
 }
}

在這個類中,帶有$var1, $var2, $var3三個屬性。$var1被聲明為private, $var2和$var3是protected.在此處

$hoge=new Hoge1;
echo』var1:』.$hoge->var1.」<br> 」

如果嘗試參照不允許從外部進行訪問的private屬性,那麼就會出現如下錯誤:

Fatal error: Cannot access private property hoge1::$var1 in /path/to/script.php on line XX

對於protected的$var2也是相同的。

但是,因為$hoge的方法是沒有private和protected的,所以下面的代碼能夠正常運作,返回內部私有和保護變量的值。

echo 'var1: ' . $hoge->var1() . "<br> "; // var1: A
echo 'var2: ' . $hoge->var2() . "<br> "; // var2: B
echo 'var3: ' . $hoge->var3() . "<br> "; // var3: C

$hoge->setLower();

echo 'var1: ' . $hoge->var1() . "<br> "; // var1: a
echo 'var2: ' . $hoge->var2() . "<br> "; // var2: b
echo 'var3: ' . $hoge->var3() . "<br> "; // var3: c

其次,為了能夠看到protected的屬性的狀態,我們試著創造了繼承了Hoge1的類Hoge2

class Hoge2 extends Hoge1 {
 public $var3 = '3';

 function d_var1() {
  return $this->var1;
 }
 function d_var2() {
  return $this->var2;
 }
 function d_var3() {
  return $this->var3;
 }
}

在 類Hoge2中,只有$var3被聲明為public。在屬性是protected的情況下,從子類進行訪問有何種限制,是由子類的屬性聲明決定的。在 Hoge2中,因為$var3被聲明是public,因此無論是從何處都可以訪問Hoge2的$var3(實體是Hoge1的$var3)。因為$ var1在Hoge1中是private,因此,在Hoge2子類中Hoge1的$var1不會被繼承,而在Hoge2中有可能會做出名為$var1的屬 性,因此,必須要明確區分Hoge1::$var1和Hoge2::$var1。

$hoge = new Hoge2;

echo 'var1: ' . $hoge->var1 . "<br> ";   // var1:
// echo 'var2: ' . $hoge->var2 . "<br> ";  // Error
echo 'var3: ' . $hoge->var3 . "<br> ";   // var3: 3

echo 'var1: ' . $hoge->d_var1() . "<br> "; // var1:
echo 'var2: ' . $hoge->d_var2() . "<br> "; // var2: B
echo 'var3: ' . $hoge->d_var3() . "<br> "; // var3: 3

$hoge->var1是與Hoge1::var1沒有關係的變量,因此不會有任何顯示,因為var2有protected訪問限制,所以如果不通過method就直接參照$var2,就會出現致命錯誤。

■引入訪問方法的限制

與上述相同,此處也分為private, protected, public三種。

· Public: 能夠從任何地方調用
· Private: 只能夠從這個類的method內調用
· Protected: 只能夠從這個類以及subclass的method中調用

此處的意思同Java和C++相同,請不要搞混。

■抽像(abstract)的類和抽像的方法

支持與Java相同的抽像類和抽像方法。抽像方法只提供了方法名的調用方式,而沒有提供實體。另外,持有抽像方法的類,必須抽像宣言類本身。如果想要直接作成抽像類的對象,那麼就會出現如下的致命錯誤。

Fatal error: Cannot instantiate abstract class ClassName

產生錯誤的實際的例子如下所示:

<?php

abstract class MyAbstract {
 abstract public function test();
 public function test2() {
  echo "MyAbstract::test2() called.<br> ";
 }
}

class MyImplement extends MyAbstract {
 public function test() {
  echo "MyImplement::test() called.<br> ";
 }
}

$obj = new MyImplement;
$obj->test();

?>

■接口(interface)

支持與Java相同的接口(interface)。接口是適合所描述的外部調用形式而設計組合起來的。
接口的實體不能夠記錄。相反的,實現接口的類必須持有與這個接口的方法相對應的實體。另外,類能夠實現多個接口,因此,有可能實現多重繼承。

<?php
interface Throwable {
 public function getMessage();
}

interface Serializable {
 public function toString();
}

class MyException implements Throwable, Serializable {
 public function getMessage() {
  return 'this is MyException message';
 }

 public function toString() {
  return 'MyException: this is MyException message';
 }
}

$e = new MyException;
echo $e->getMessage();
echo $e->toString();
?>

■final聲明

同Java一樣,PHP5支持final聲明。如果對於一個方法追加final聲明,這個方法將肯定在子類不能重載(Override)。如果方法被final聲明了,但是還在子類中重載,就會出現如下錯誤:

Fatal error: Cannot override final method fuga::foo()

產生錯誤的例子:
<?php
class Fuga {
 final function foo() {
  echo "this is final function ";
 }
}

class Hoge extends Fuga {
 function foo() {
  echo "this is not final function ";
 }
}
?>

[摘要]目前開發中的PHP5,其面向對象的機能已經被大幅度的強化了。下一代的PHP將會是怎樣的一種語言呢?下面我們來詳細講解一下目前發佈的PHP5的beta release。




(三) PHP5的新特性(續)

PHP5的發佈計劃

在前面的文章中我們提到,「根據ZEND公司2003年4月1日發佈的訊息的話,現在的應該就是Beta Release了」,但是開發者內部討論的結果是,Beta為時尚早,而且有可能不是Beta Release.

對這方面動向有興趣的可以參照 news://news.php.net/ 上所公佈的信息 php.version5.dev:372

在這個文件中,PHP5的發佈計劃又重新回到了一張白紙,而另一方面,Zend Engine2的開發正在著手進行中。PHP5的Release其實大體就是盼望著「快點到年終吧」。

PHP5的新特性

接著我們來看一下在前面所講到的其他一些關於類的新增的機能

■名空間

PHP5支持名空間。因此,我們可以在名空間內裝入類、變量、常量、函數。

在PHP4的Scope中,只有global、函數內、類內這三個種類,所以要特別注意如果不注意的話,將會很容易「污染」global空間。假如使用名空間的話我們就能夠在package裡分離變量命名空間,因此應該就能比較容易的做成獨立的package。

使用實例如下:

namespace This {
 class Hoge {
 }
 const aConstant = 'This Constant';
 function aFunction() {}
 var $aVariable = 'This Variable';
}

$obj = new This::Hoge;
echo This::aConstant . "<br> ";
This::aFunction();
echo This::$aVariable . "<br> ";

假設要訪問名空間內的對象的話,就應該這樣做:

名空間名::對像名

但是PHP5的名空間不會套入與C++相異的樣子。

■Class內常量

使用關鍵字const,能夠在類、名空間內定義常量。這裡因為是常量,因此一定要在常量名的前面加上$。Class內的常量,比這個類中的global常量的優先級要高。

在這裡const是預約語,因此在class名和函數名中使用const的時候要做必要的修正。

<?php
define('constant_value', 'global constant');

class MyClass {
 const constant_value = 'class constant';

 function printConstant() {
  print constant_value;
 }
}

echo MyClass::constant_value . "<br> ";
MyClass::printConstant();
?>

在 這個例子裡,MyClass::printConstant()是顯示常量constant_value的值,但是,constant_value存在於 global空間和Class內這兩個地方。在這種情況下,MyClass內的常量constant_value的優先級較高,被顯示為「class constant」。

■對像變量

即使是在類沒有被實例化狀態下,對像變量也能準確的按照指定的值被初始化。訪問的方法如下:

類名::$變量名

<?php
class Hoge {
 static $my_static = 5;
}

print Hoge::$my_static;
?>

■統一構建器

在生成對象的時候,能夠自動被調用的方法被稱作「構建器」。

PHP4中的構建器,是與Class名相同的方法名。這是與Java和C++相同的地方,因此,對於一些用慣了的人來說,不會有彆扭感。但是,如果要從子類中調用父類的構建器的話,在PHP中就必須特意寫上父類的名字。

在PHP中,父類的構建器不能被自動調用,因此,情況就比較多。
在PHP5中,統一採用了__constructor這個構建器名稱,不管class名是什麼,凡是被稱為__construct()的,都被當作構建器來處理。

另外,考慮到同PHP4的互換性,假如存在於Class名相同的以前的構建器名,那麼,優先使用那個構建器。

<?php
class BaseClass {
 function __construct() {
  print "In BaseClass constructor ";
 }
}

class SubClass extends BaseClass {
 function __construct() {
  parent::__construct();
  print "In SubClass constructor ";
 }
}

$obj = new BaseClass();
$obj = new SubClass();
?>

■析構函數

與構建器相反,能夠在對像釋放時自動被調用的方法被稱為析構函數。

PHP4支持析構函數,通過登錄在PHP運行終止時用register_shutdown_function()調用的函數,只有類似的實行方法。PHP5正式支持析構函數,能夠在類中指定對像釋放時的動作。

析構函數就是名為__destruct的方法。當對像內部的參照計數器變成0的時候,__destruct()被調用,然後對像所使用的內存被釋放出來。

<?php
class MyDestructableClass {
 function __construct() {
  print "In constructor ";
  $this->name = 'MyDestructableClass';
 }

 function __destruct() {
  print 'Destroying ' . $this->name . " ";
 }
}

$obj = new MyDestructableClass();
?>

另外,與構建器相同的地方是,父類的析構函數不能被自動的調用,必要的時候,需要用命令:

parent::__destruct();

■訪問

在PHP4中,如果訪問了一個不存在的屬性,那麼系統就會自動生成與之相對應的新屬性。

class Hoge {
}

$obj = new Hoge;
$obj->prop = "This is new property";

如上所示,當把值代入一個不存在的屬性時,那個代入點就會自動生成一個新屬性。同樣的,訪問一個不存在的屬性,就如同被代入NULL值的變量一樣,不會發生錯誤。

在PHP5中追加了一點,就是能夠對訪問任意的屬性進行控制。在類中如果存在__set()、__get()這樣的方法,替代上述的動作此處的方法將能夠被調用。例如:

<?php
class Hoge {
 function __set($name, $value) {
  print "__set() is called with ($name, $value) ";
  $this->$name = $value;
 }
}

$obj = new Hoge;

$obj->a = '123';
$obj->a = '456';
$obj->b = '789';
?>

在這裡,__set 方法被作為未定義屬性的代入方法,在顯示值之後將值代入未定義屬性。

$obj->a = '123';

執行這一句時,因為在這個時候不存在屬性a,因此,作為代替,__set 方法被調用。

__set() is called with (a, 123)

其次,

$obj->a = '456';

再一次的代入$obj->a,這一次,由於屬性a已經存在,所以__set 沒有被調用,和通常一樣把值代入到了屬性a中去了。

$obj->b = '789';

這一回,我們把值代入另一個屬性b中,同a的第一次情況一樣,

__set() is called with (b, 789)

同__set 方法相反,__get 方法是在對不存在的屬性的引用時調用的。將這兩者結合起來,再來看一下對於屬性的訪問,實際上,利用它能夠寫出在不同的場合都能做出不同響應的類來。

<?php
class Hoge {
 public $properties;

 function __set($name, $value) {
  $this->properties[$name] = $value;
 }
 function __get($name) {
  return $this->properties[$name];
 }
}

$obj = new Hoge;

$obj->a = '123';
$obj->b = '456';
echo $obj->a;
echo $obj->b;

print_r($obj);
?>

在 這個例子裡,對類中所有屬性的訪問被裝入了$properties中,這樣,使我們加入的屬性不直接的附在對像之下。這是個不太能容易理解的例子,例如, 試著把這個例子中的保存到$properties改成存入文件或是數據庫會很有趣吧。實際上,在對像裡面,我們能夠簡單的實現讓許多的複雜的操作。

與__set, __get多少有些不同,但是__call也能用來書寫不存在的方法,當我們向如下例子一樣調用對象的方法的時候,

$object->methodname();

如果這個類中不存在methodname這個方法,通常情況下,就會出現如下錯誤:

Fatal error: Call to undefined method Class::methodname()

但是,如果這個類中存在__call這個方法,作為替代,__call就被調用。__call的參數有兩個,第一個參數是被叫出的方法名,第二個參數是保持了的被調用的參數的數組。考慮到有很多的使用方法,除了以下的例子外,還可以使用其它的方法。

<?php
class Proxy {
 private $object;

 function __call($name, $params) {
  if (isset($this->object)) {
   if (method_exists($this->object, $name)) {
   return call_user_func_array(array($this->object, $name), $params);
   }
   else {
   return "method not exists.";
   }
  }
 }
 function __construct($object) {
  $this->object = $object;
 }
}

class Hoge {
 function add($var1, $var2) {
  return $var1 + $var2;
 }
}

$p = new Proxy(new Hoge);

$result = $p->add(1, 2);
echo "result: $result<br> ";

$result = $p->sub(5, 3);
echo "result: $result<br> ";
?>



  與類相關的一些擴充的功能大體上都講過了。下面,我們再來講述一下類以外的擴充的功能。

■參數的值修改後可以傳回

  有時候我們會想要收回函數的參數,「想要寫回數組」等等,而在PHP4中,我們是不能夠設定參照的參數的默認值的。

  在PHP5中,這一點就能夠得以實現。另外,當對像為參數的時候,正如前編裡講的一樣,即使不加上」&」也能夠參照過渡,所以不需要特別注意。

 function some_func(&$var == null) {
  if ($var == null) {
   // 不需作特殊處理
  }
 }
?>

■__autoload()

   當我們使用不存在的類的程序的時候,通常是以error告終。但是,假如存在__autoload()這個函數的話,我們就可以在錯誤發生前調用這個函 數。在__autoload()內,如果讀入include_once等類的定義文件,就可以不發生錯誤繼續進行處理。

  在大規模的PHP站點,必定會有在一次運行中讀入大量文件的情況發生,因為只讀入了必要的類,所以減輕了負荷,__autoload也許會很好用也不一定啊。看看下面的例子,自動讀取所需要的類的定義文件:

function __autoload($className) {
 include_once $className . '.php';
}

$object = new ClassName;
?>

■例外處理

  在編寫代碼的時候,錯誤的處理是避而不談的事項。因為在每次的錯誤的時候去檢查函數和方法中返回的值的確實是一件麻煩的事情。為了避免這些麻煩,就有了把例外處理當作文法來看待的語言。而PHP5就支持這個功能的語言。

   在文法方面,同Java和C++一樣,我們流行用catch{}這個命令來處理用try{}這個命令投入的例外。但是,同上述的兩種語言不同的是, PHP5的例外方法中只能投入對象。另外,在用catch{}獲取例外的時候,必須要靈活指定所獲取的例外類型。所以在使用的時候,一般會使用複數的 catch{},以避免漏掉的例外。

  由於對例外處理的支持,形成了try、throw、catch這三個預約語。所以原有的PHP4代碼中如果有使用這三個詞作函數名的,必須作出修正。

看一下下面的例子,使用自定義的例外。

class MyException {
 function __construct($exception) {
  $this->exception = $exception;
 }
 function Display() {
  print "MyException: $this->exception ";
 }
}

class MyExceptionFoo extends MyException {
 function __construct($exception) {
  $this->exception = $exception;
 }
 function Display() {
  print "MyExceptionFoo: $this->exception ";
 }
}

try {
 throw new MyExceptionFoo('Hello');
}
catch (MyException $exception) {
 $exception->Display();
}
?>

■回溯

  從PHP-4.3.0開始支持debug_backtrace函數。因為是使用Zend Engine 2.0開發的,能夠在PHP4中把有用的東西back trace出來。使用這個函數的時候,能夠取得在這個時點調用函數的信息。

詳細的請參閱一下網址:http://www.php.net/manual/zh/function.debug-backtrace.php

■與PHP4的兼容性

  講到與PHP4的兼容性,正如前面幾篇講到的,雖然文法有了很大的變化,但在保留了向下兼容的同時,還補充一些關鍵字。還有在使用常量和函數、類等等的時候要都做出了必要的修正。

新增加的關鍵字:

  public、protected、private
  abstract、interface、implements
  final、namespace、import、const
  try、throw、catch

   即使是關於對像功能,筆者也剛剛試了一下手頭的幾個類,並沒有什麼問題。但是也沒有全部都運行一遍。例如,pear命令在現在的PHP5中不能夠很好的 運作。與pear命令並列的PEAR Framework,因為能夠活用PHP4的對象機能,即使不改也是很不錯啦。(笑)

  因為PHP5的說明


Comments

Leave a Reply

 authimage