PHP 5.2已於2006年 11 月2 日正式發布。新發布的 PHP 5.2 除修復了以往200多個BUG以外,還進一步提高性能,尤其是改善了在高負載情況下的表現;在安全性的處理上也做出了很大的改進,同時也增加了很多很實用的技術(比如 JSON、Zip等支持),另外還有一些原本定為 PHP6 的特性也已經被提前實現在這個版本當中。
PHP 5.2的新特性:
1、移除了filepro 和 hwapi 兩個擴展(這兩個被移動到了 PECL)的同時另外增加了4個新的擴展:Date、JOSN、Filter和ZIP;
從 PHP 5.1 開始,在 PHP 核心增加了一個 Date 擴展,重寫了對日期/時間(主要是時區方面)的支持。PHP 5.2 更進一步,日期和時區則分別成為了類 DateTime 和 DateTimeZone 的一個對象。大家可以在 PHP CLI 中運行php –rc DateTime 或 php –rc DateTimeZone 來看一下這兩個類的詳細信息。需要注意的是如果你的程序中已經存在有名為DateTime 或 DateTimeZone 類的話,那想在 PHP 5.2 中運行就必須改名了。
JSON 擴展實現了則實現對 Javascript Object Notation (JSON) 這種輕量級數據交換格式的支持。在 PHP 5.2 中這個擴展是默認被啟用的。很多開發人員都為 PHP與 Javascript 之間的通信發愁過,尤其是現在這個流行 AJax 的時代。而現在,我們用JSON 就可以很輕易的解決這個問題。JSON數據能夠直接為服務器端代碼使用, 並且也能夠讓客戶端的 JavaScript簡單地通過 eval() 來進行讀取,這就大大簡化了服務器端和客戶端的代碼開發量。雖然以前在 PHP 裡面也有一些 JSON 類來支持,但這哪有 PHP 的原生支持來得高效和快捷呢?
Filter 擴展負責校驗和過濾數據,這個擴展主要是為了處理像用戶的輸入那樣的不可靠數據而設計的。這個擴展也是默認被啟用的。默認情況下的RAW 模式將不會以任何方式影響輸入的數據,也就是說這不會對現有代碼產生任何影響。但我們在以後的開發中應該盡可能地利用這個擴展來進行敏感字符的過濾,因為這不但簡化了一些表單的驗證工作,而且提高了程序的安全性和運行效率。
ZIP 擴展將允許我們對 Zip 壓縮包及其包內的文件進行讀寫操作。也就是說,我們對 Zip文件不僅能看,而且還能摸:它提供了對 Zip 文件的完全支持。
2、改進了內存管理器,使之在高負載情況下具有更佳的表現。
按照 “Zend 二老”之一也就是 “Zend”中那個“nd”的說法,這個新的內存管理器是分層(hIErarchical)的。這個管理器共有三層:存儲層(storage)、堆(heap)層和emalloc/efree層。存儲層通過malloc()、mmap()等函數向系統真正的申請內存,並通過 free() 函數釋放所申請的內存。存儲層相當於Zend 自己的“內存倉庫”,通常申請的內存塊都比較大。堆層(注意這裡的堆並不是指操作系統所管理的堆,而是 Zend 內存管理器的所管理的內存堆)就把它們從存儲層要過來並分割成一些較小的塊。而 emalloc/efree層就是指通過 PHP API的emalloc()/efree() 函數所申請和釋放的內存。emalloc() 並不直接同存儲層打交道,同它們接頭的是堆層。負責把比原來的管理器在同等條件下會分配更小的內存,但速度更快。它首先會從系統(堆)中申請一些較大的內存塊,然後自己來管理這個堆。PHP.ini 中的memory_limit 值雖然還會被檢用,但已不再是每次emalloc() 調用時都檢用,而僅僅是在向系統申請那些大的內存塊時被檢用。
熟悉一些服務器端編程的朋友可能會馬上想到一個詞:內存池!沒錯,這基本上就是一個內存池。內存池技術其實這是服務端編程中很常見。使用內存池技術可以有效地避免頻繁的內存申請/釋放操作,在內存池技術中,內存釋放時實際上並沒有通知操作系統真實的釋放和回收,而僅僅是對將要釋放的內存做個了標記,表示該部分內存已經不再使用。等下一次申請內存時,就從這些“可用”的內存鏈表中取出一個內存塊,從而避免了頻繁的內存申請/釋放操作,大大節省了系統資源。根據測試和統計,在 PHP 4.4版本中,一個典型的較為簡單的請求就有超過 20,000次的對系統堆的申請和釋放操作,這花費的時間相當於整個腳本所花費時間的 20% 左右。由此可見,若能降低這種資源消耗則效果是極為可觀的。內存池技術還有一個不太引人注意的好處:它能降低內存洩漏的概率。不過采用內存池技術的管理器也明顯要比常規管理器(指的是內存即時申請,即時釋放的管理機制)的內存開銷要大,因為除了真正申請的內存外,管理器還得負責維護每個內存塊的狀態,因此 PHP 5.2 中把 PHP.ini中 memory_limit 指令值從默認的 8M 提升為 16M 。
3、PHP 5.2 也對 INI 指令的存取方面做了優化。
在 PHP 腳本中我們可以用 ini_set() 函數動態改變某個 PHP 指令的值。但問題是在請求結束後你還得把這些改變的值給恢復過來以保證下個腳本能正確使用。在 PHP 5.2 之前,PHP 的做法可謂是不辭勞苦,逐個遍歷 INI 指令並恢復的。如果你是整個腳本都是 ini_set() 也就罷了(當然這個情況也是極其罕見的。:)),萬一我的腳本中很少使用甚至根本就沒有使用這個函數那我不就是虧大了?因此 PHP 5.2 為了解決這個問題又額外增加了一個表專門保存更改過的指令,這樣就不用來回挨個恢復了。
4、對 require_once() 和 include_once() 兩個函數進行優化。
PHP 5.2 以前 require_once() 和 include_once() 的做法是無論某個文件是否已經被緩存或編譯過,統統是先 fopen() 再說,打開成功後在查詢一下是否已經緩存過。這麼處理的原因就是 在 PHP 5.1 以前沒有很完美的解決 realpath() 相對路徑和符號連接方面的問題。因為若不能唯一地正確地確定某個路徑的真實路徑表示那麼你就無法利用這個路徑的唯一性去解決某個問題。而 fopen 則沒有這個顧慮。realpath() 的這個問題在 PHP 5.1 中被徹底搞定了,但還沒來得及應用到 require_once() 和 include_once() ,結果就推遲到現在。解決這個問題的好處是在於避免了 fopen 這個 I/O 操作,在很多高負載情形中,通常都是數據庫、網絡或者磁盤 I/O 而不是 CPU 成為瓶頸。
5、對HashTable 的復制進行了優化。
HashTable在ZendEngine是一個很基本的數據結構,數組本質上就是一個 HashTable。對HashTable 的優化也將意味著對數組的復制操作(無論是顯式還是隱式)速度將會有一定的提升。
6、else
PHP 5.2 也對在FastCGI SAPI 模塊中訪問環境變量的性能做了少許優化。以前則是逐行搜索,現在則是通過 Hash 值來存取。除此之外,PHP 5.2還對str_replace() 和 implode() 函數以及“try {} catch {}”塊等都做了一定的優化。
對一些語言特性和安全特性方面的改進:
1、繼PHP 5.0增加了一個 E_STRICT的錯誤報告級別(常量值為 2048)之後,PHP 5.2 也新增了一個錯誤報告級別:E_RECOVERABLE_ERROR ,其常量值為 4096 。
這個級別的錯誤主要是從E_ERROR中但可以被用戶自定義的錯誤處理程序(一般通過set_error_handler() 函數指定)所捕捉的情況轉化而來。如果一個E_RECOVERABLE_ERROR 未被捕捉並處理,那麼它的表現就和所有 PHP 版本中的E_ERROR一樣會導致程序中止。在錯誤日志中,該類型的錯誤將被記錄成“可捕捉的致命錯誤(Catchable fatal error)”。
導致 PHP 拋出 E_RECOVERABLE_ERROR 的情況通常是指那些很危險,但還不足以讓 Zend Engine 崩潰的情況。比方說,有下面一段代碼:
class foo {
function bar(foo $a) { }
$a = new foo ;
$a->bar(new stdClass) ;
很明顯,類 foo 的bar 函數要求一個 foo 類型的參數,但實際代碼中卻給了一個 stdClass 類型的參數。在PHP 5.2 以前,這會導致一個 E_ERROR(Fatal error: Argument 1 passed to foo::bar() must be an instance of foo……)。但在 PHP 5.2(包括以後的 PHP6)則會導致一個E_RECOVERABLE_ERROR(Catchable fatal error: Argument 1 passed to foo::bar() must be an instance of foo……)。這種錯誤是可以被捕捉的,如果你通過set_error_handler() 指定了一個錯誤處理函數(即使是你在這個函數中沒有處理E_RECOVERABLE_ERROR),那麼程序就會繼續運行。但如果你沒有指定一個錯誤處理函數,那麼這個 E_RECOVERABLE_ERROR 錯誤就會和 E_ERROR 一樣,會立即導致程序中止。
2、相應的,錯誤報告級別 E_ALL 也將會包含上述E_RECOVERABLE_ERROR。
這也就意味著常量 E_ALL 的值將會從原來的2047 變為 6143。注意,在 PHP 5.0和 PHP 5.1中雖然增加了E_STRICT,但在這兩個版本中 E_ALL 並不包含E_STRICT。而在以後的版本(如 PHP5.2、PHP6等)中E_ALL 則包含了包括E_STRICT和E_RECOVERABLE_ERROR在內的所有錯誤級別。 在PHP 5.0/5.1 中我們想設置error_reporting為E_ALL就不得不采用error_reporting(E_ALL | E_STRICT) 的寫法,感覺極為別扭,也很容易造成一些疏忽和誤導。在 PHP5.2當中我們就沒有這個苦惱了。另外如果你在 apache 的配置文件(如 httpd.conf)或 .htAccess 文件中用error_reporting 設置了錯誤報告級別(比如:php_value error_reporting 4095),由於 apache 不支持 PHP 常量,那你還得手工去適當調整這些錯誤報告級別的數值。
3、添加了allow_url_include 這個 ini 指令來輔助 allow_url_fopen 操作;這個是 PHP 5.2 在安全方面的重大更新之一。使用這個指令可以讓我們區分開對遠程文件的標准文件操作和包含操作。我們通常需要進行前面的標准操作,而後面的包含操作則通常是危險的發源地。從 PHP 5.2 開始,你的本地腳本可以在禁止遠程包含操作的同時進行標准遠程文件操作。事實上,這個就是默認配置。 PHP 5.2 把原來的allow_url_fopen 指令分成了allow_url_fopen 和allow_url_include 兩個指令。如果allow_url_fopen 操作是禁止的,那麼allow_url_include 也將被禁止。默認情況下將會允許進行 allow_url_fopen 操作,但是禁止allow_url_include 。這樣就能非常有效的避免遠程代碼注入(remote code injection)。這個本來也是打算在 PHP6中添加的。
4、PHP 5.2 增加了對接口中構造函數類型(簽名) 強制性檢查的支持。
從 PHP 5.2 開始,如果你在一個接口中聲明了一個構造函數,那麼在所有實現該接口的類都必須包含一個構造函數,並且這個構造函數要與該接口的構造函數的簽名完全一致。這裡術語“簽名”的意思是函數的參數和返回值的類型(包括其語言類型以及是引用傳遞還是值傳遞),這個概念有點類似於C 語言中 “原型”。
看以下代碼:
interface constr {
function __construct() ;
class implem implements constr {
function __construct ($a) {
這段代碼在 PHP5.0和 PHP 5.1 裡面運行是毫無問題的,但在 PHP 5.2中則會拋出一個錯誤:Fatal error: Declaration of implem::__construct() must be compatible with that of constr::__construct(),提示類implem 的構造函數與接口constr 的構造函數的聲明不匹配。
值得一提的是這項新特性的添加過程是很有意思。,有興趣的朋友可以到Zend 的每周總結(http://www.zend.com/zend/week/week279.PHP#Heading9)裡面看看來龍去脈,此處不再贅述。
5、__toString() 函數將會在任何合適的地方被調用。
魔術方法 __toString() 現在會在一個字符串上下文環境中被調用。換句話說,一個對象在任何地方都可以作為一個字符串來使用,只要它實現了 __toString() 函數。當然你實現的 __toString() 函數不能拋出異常,否則腳本將會中止運行。以前為防萬一,PHP 5.0/5.1會在必要時會把對象標識(Id)作為一個字符串返回,這個特性在 PHP 5.2中已經被拋棄。因此這帶來的問題就可能是不能保證一個對象的標識總是唯一的。如果你在程序中利用了對象標識符的唯一性,那這將會是某種缺陷。如果沒有實現類的 __toString()函數但卻把其對象作為作為一個字符串來使用就會導致一個“可捕捉的致命錯誤”。 還有個特例,就是對象也不能作為作為數組的索引或者鍵名,即使是它有一個 __toString() 方法。以後 PHP 可能會內建一個 Hash機制來提供對對象唯一性的支持,但就現在來說,你必須自己提供一個對象的 hash 算法,或者干脆就用新提供的 SPL 函數:spl_object_hash();
6、為在寫模式下訪問 __get() 的返回值這種情況增加了E_NOTICE 級錯誤提示。
顯然 __get() 函數只能在讀模式下返回一個值,並且也不可能把一個值寫入 __get() 函數。但在以前的版本中並沒有為這種不正確的用法給予提示。從 PHP 5.2 開始將會為這種情況拋出一個E_NOTICE。注意:如果你對 foreach() 和其他的一些更改數組內部指針的函數也采取了同樣的操作(即給 foreach 所“抽”出來的值進行賦值),那也會觸發一個E_NOTICE ,因為這些“抽”出來的值都是處於讀模式。如果你的代碼中存在這種情況,那你應該把 __get() 函數的返回值轉換為一個數組,或者用 SPL 裡面的ArrayObject 來代替這個數組。
7、丟棄了抽象靜態的類函數。
由於“筆漏”,PHP 5.0和5.1 版本竟然允許類具有抽象靜態函數,不過現在不行了。現在只允許接口有抽象靜態函數。
8、其他一些語言特性的改變:
- SPL 新增了正則迭代器(Regex Iterators)、文件對象(SplFileObject)的CSV 支持等。
- 增加了對 RFC2397 (數據流)的支持。
- 增加了對apache 2.2的支持。
- 現在可以在上傳文件時實時取得文件的上傳進度了。
- 對PHP或其擴展所需 OpenSSL 庫、PCRE 庫、MySQL客戶端庫、PostgreSQL 客戶端庫、SQLite 庫等均進行更新升級。