PHP 5.4 中的新特性

2013-05-21 14:42:56來源:Oracle作者:Rasmus Lerdorf

大約八年前,我為 Oracle 技術(shù)網(wǎng)寫了一篇名為“您了解 PHP 嗎?”的文章。在那篇文章中,我談到了 PHP 固執(zhí)的功能優(yōu)于形式的“Web 問題”解決方法,以及它所具備的讓事情變得簡單的能力。當時,我們即將發(fā)布 PHP 5.

大約八年前,我為 Oracle 技術(shù)網(wǎng)寫了一篇名為“您了解 PHP 嗎?”的文章。在那篇文章中,我談到了 PHP 固執(zhí)的功能優(yōu)于形式的“Web 問題”解決方法,以及它所具備的讓事情變得簡單的能力。當時,我們即將發(fā)布 PHP 5.0,F(xiàn)在,時隔將近十年之后我們推出全新的 PHP 5.4.0 版本,雖然在這期間發(fā)生了很多事情,但也有許多事情根本沒變。

沒變的一件事情就是生態(tài)系統(tǒng)一如以往那樣重要。解決 Web 問題不僅僅關(guān)乎腳本語言的選擇,它關(guān)乎的是周圍的整個生態(tài)系統(tǒng),F(xiàn)在,LAMP 體系已盛行近 15 年,仍廣受歡迎,但我們開始注意到其他功能強大的方案。附帶 nginx 的 PHP-FPM 已經(jīng)快速流行起來,因為從 PHP 5.3 開始大大改進了支持,并在 5.4 中進一步得到簡化。體系中的 M(即數(shù)據(jù)庫)部分與 8 年前相比也開始變得極為不同。與將所有內(nèi)容都只放入 MyISAM 表中相比,各種 NoSQL 解決方案和 MySQL Cluster 提供了一組更豐富的選擇。

出現(xiàn)了多種有趣的技術(shù),因而我們編寫了 PHP 擴展來輕松訪問這些技術(shù)。我最喜愛的一個擴展是 libevent,可以用它在 PHP 中編寫事件驅(qū)動的高性能應用程序。另一個是 ZeroMQ,這是一個高級套接字庫。與 SQLite 不再需要編寫另一種原始文件格式和關(guān)聯(lián)的分析器極為相似,ZeroMQ 也無需因任何理由而使用套接字協(xié)議和關(guān)聯(lián)的套接字處理代碼。您甚至可以組合使用 libevent 和 ZeroMQ,以獲得獨立、高性能、事件驅(qū)動的卓越服務器。(如果您對此感興趣,請參見此示例。)我還十分喜歡 SVM(支持向量機)這一機器學習算法,您不必成為機器學習的愛好者也可提出許多問題。

還有許多擴展在最近幾年內(nèi)已被廣泛接受。特別是,Gearman 變得流行起來,逐漸成為用戶部署的常見體系的一部分。您可以通過 Gearman 分派作業(yè),以便由工作器異步完成這些作業(yè)。工作器可以遍布多臺服務器,它們甚至可以進一步分派更多的 MapReduce 類型作業(yè)。

2004 年發(fā)布 PHP 5.0 之后,接下來在 2005 年推出 5.1,此版本新增了 DateTime 實現(xiàn)、PDO 和性能改進。PHP 5.2 于 2006 年發(fā)布,引入了改進的內(nèi)存管理器、JSON 支持和輸入篩選。當時,我們著手推動 PHP 6,這是一個極其宏偉的計劃,完全重寫有關(guān) ICU(Unicode 國際化組件)庫的所有內(nèi)容。事實證明這個計劃有些操之過急 — 我們無法使足夠多的開發(fā)人員為之興奮,最終只得將準備引入 PHP 6 的各種特性添加到 2009 年發(fā)布的 PHP 5.3 中。5.2 與 5.3 版本時隔 3 年,這也意味著 5.3 向 PHP 新增了大量內(nèi)容:命名空間、后期靜態(tài)綁定、閉包、垃圾收集、受限 goto、mysqlnd(MySQL 原生驅(qū)動程序)、更好的 Windows 性能以及許多其他內(nèi)容。

事后看來,將此版本稱為 PHP 6 可能有一定的道理,但 PHP 6 等同于在 Unicode 方面所做的努力,以至于為此編寫了相關(guān)書籍,因此我們認為如果沒有對 Unicode 做出重大改進,就不能發(fā)布 PHP 6。我們引入了名為“intl”的 ICU 擴展,它也針對 PHP 5.2 編譯,這可讓您訪問更多的 ICU 功能。mbstring 擴展隨時間不斷地改進,這意味著幾乎任何與 Unicode 相關(guān)的問題都有解決方案,只是未明確集成到語言本身中。

這樣在 2012 年推出 PHP 5.4。而且,與上一版本時隔將近 3 年,我們在此期間對一些內(nèi)容進行了改進。我寧愿恢復到每年推出一個版本,每個版本包含更少的新特性。

以下是您升級到 5.4 時將看到的主要特性:

內(nèi)存和性能改進

許多內(nèi)部結(jié)構(gòu)已變得更小或完全消失,從而在大型 PHP 應用程序中可節(jié)省 20-50% 的內(nèi)存。通過各種優(yōu)化使性能提高 10-30%(主要取決于代碼執(zhí)行的操作),這些優(yōu)化包括內(nèi)聯(lián)各種常見代碼路徑、將 $GLOBALS 添加到 JIT、“@”操作符運算更快、添加了運行時類/函數(shù)/常量緩存、運行時字符串常量現(xiàn)在被拘留、通過預先計算的散列更快地訪問常量、空數(shù)組速度更快并使用更少內(nèi)存、unserialize() 和 FastCGI 請求處理速度更快,以及在整個代碼中進行更多的內(nèi)存和性能調(diào)整。

例如,早期的一些測試表明,Zend Framework 在 5.4 中運行速度提高 21% 并且內(nèi)存使用減少 23%,而 Drupal 內(nèi)存使用減少 50% 并且運行速度大約提高 7%。

Trait

Trait 可能是 PHP 5.4 中談論最多的特性 — 將它們視為編譯器輔助的復制粘貼。Trait 也是 Scala 的一個特性。其他語言可能將它們稱為“mixin”— 或者這些語言根本不對它們進行命名,但具有擴展接口機制,允許接口包含其方法的實際實現(xiàn)。

與 mixin 相反,PHP 中的 trait 包括顯式?jīng)_突解決機制,用于多個 trait 實現(xiàn)相同方法的情況。

trait Singleton {
    public static function getInstance() { ... }
}

class A {
    use Singleton;
    // ...
}

class B extends ArrayObject {
    use Singleton;
    // ...
}

// Singleton method is now available for both classes
A::getInstance();
B::getInstance();

請參見 php.net/traits 了解更多示例,包括沖突解決語法、方法優(yōu)先順序、可見性以及對 trait 中常量和屬性的支持。此外,要詳細了解概念理論,您可以閱讀 Nathan Schärli 的論文“Trait:行為構(gòu)建塊中的組合類”。

精簡數(shù)組語法

新增的一種簡單但非常流行的語法:

$a = [1, 2, 3];
$b = ['foo' => 'orange', 'bar' => 'apple'];

就是說,您現(xiàn)在不再需要使用“array”關(guān)鍵字來定義數(shù)組。

函數(shù)數(shù)組解除引用

新增的另一種常用語法。返回數(shù)組的函數(shù)調(diào)用現(xiàn)在可以直接解除引用:

function fruits() {
    return ['apple', 'banana', 'orange'];
}
echo fruits()[0]; // Outputs: apple

實例方法調(diào)用

與函數(shù)數(shù)組解除引用相關(guān),您現(xiàn)在可以調(diào)用對象實例化方法。與早期版本一樣,您當然仍可以鏈接方法調(diào)用,因此您現(xiàn)在可以編寫如下代碼:

class foo {
    public $x = 1;
 
    public function getX() {
        return $this->x;
    }
    public function setX($val) {
        $this->x = $val;
        return $this;
    }
}
 
$X = (new foo)->setX(20)->getX(); 
echo $X; // 20

然而,由于可能丟棄實例化的對象,因此,除非您的構(gòu)造函數(shù)執(zhí)行有用操作,否則您應該在此改用靜態(tài)方法調(diào)用。如果將它與精簡數(shù)組語法和函數(shù)數(shù)組解除引用結(jié)合使用,我們可以編寫某些十分復雜的代碼:

class foo extends ArrayObject {
    public function __construct($arr) {
        parent::__construct($arr);
    }
}
 
echo (new foo( [1, [4, 5], 3] ))[1][0];

看一眼之后,您可以斷定輸出是什么嗎?在此,我們將二維數(shù)組傳遞到僅返回數(shù)組的構(gòu)造函數(shù)。然后,我們選出第二個維度的第一個元素,因此這將輸出“4”。

閉包綁定

閉包是在 PHP 5.3 中引入的,但在 5.4 中我們改進了閉包與對象的交互方式。例如:

class Foo {
  private $prop;
  function __construct($prop) {
    $this->prop = $prop;
  }
  public function getPrinter() {
    return function() { echo ucfirst($this->prop); };
  }
}

$a = new Foo('bar');;
$func = $a->getPrinter();
$func(); // Outputs: Bar

注意閉包訪問 $this->prop 這一私有屬性。默認情況下,PHP 中的閉包使用預綁定 — 這意味著閉包內(nèi)的變量具有定義閉包時所具有的值。可以使用引用將其轉(zhuǎn)換為后綁定。但是,也可以重新綁定閉包:

$a = new Foo('bar');
$b = new Foo('pickle');
$func = $a->getPrinter();
$func(); // Outputs: Bar
$func = $func->bindTo($b);
$func(); // Outputs: Pickle

在此,我們將閉包從 $a 實例重新綁定到 $b 中的實例。如果您不希望閉包隨時訪問對象實例,可以將閉包聲明為靜態(tài):

class Foo {
  private $prop;
  function __construct($prop) {
    $this->prop = $prop;
  }
  public function getPrinter() {
    return static function() { echo ucfirst($this->prop); };
  }
}

$a = new Foo('bar');;
$func = $a->getPrinter(); 
$func(); // Fatal error: Using $this when not in object context

對象即函數(shù)

有一種新的神奇方法,名為“__invoke”,其用法如下:

class MoneyObject {
    private $value;
    function __construct($val) {
        $this->value = $val;
    }
    function __invoke() {
        return sprintf('$%.2f',$this->value); 
    }
}
$Money = new MoneyObject(11.02/5*13);
echo $Money(); // Outputs: $28.65

內(nèi)置 Web 服務器 (CLI)

CLI 服務器是一種小型 Web 服務器實現(xiàn),可以從命令行運行:

% php -S localhost:8000
PHP 5.4.0 Development Server started at Sun Mar 11 13:27:09 2012
Listening on localhost:8080
Document root is /home/rasmus
Press Ctrl-C to quit.


CLI 服務器不適合用作生產(chǎn) Web 服務器;我們將使用它運行一些 PHP 回歸測試,其他單元測試機制也可使用它,并且 IDE 也可能使用它。它確實具有一些很有用的特性,用于從命令行進行日常代碼調(diào)試。默認情況下,它使用當前目錄作為 DocumentRoot;它也處理靜態(tài)文件請求。默認目錄索引文件為“index.php”,因此您可以在滿含 .php、.css、.jpg 等文件的目錄中激活它,它自己就可以運行。對于可能使用 mod_rewrite 將所有請求發(fā)送到前端控制器或路由器的更復雜應用程序,您可以將此路由器與一個簡單的小腳本包裝在一起,并啟動 CLI 服務器,如下所示:

% php -S localhost:8080 /path/to/router.php
PHP 5.4.0 Development Server started at Sun Mar 11 13:28:01 2012
Listening on localhost:8080
Document root is /tmp/web
Press Ctrl-C to quit.


router.php 腳本可能如下所示:

<?php
if (preg_match('!\.php$!', $_SERVER["REQUEST_URI"])) {
    require basename($_SERVER["REQUEST_URI"]);
} else if (strpos($_SERVER["REQUEST_URI"], '.')) { 
    return false; // serve the requested file as-is.
} else {
    Framework::Router($_SERVER["REQUEST_URI"]); 
}

此包裝器加載直接 .php 請求,將包含“.”的任何其他請求傳遞到靜態(tài)文件處理程序,其他所有內(nèi)容都傳遞到框架的路由器。您可以如此直接從命令行運行 Drupal 和 Symphony。

原生會話處理程序接口

這是一個小而方便的特性,現(xiàn)在可以用它實現(xiàn)會話處理程序接口,F(xiàn)在,您可以僅將會話處理對象的實例傳遞給 session_set_save_handler(),而不必傳遞給它六個比較麻煩的函數(shù):

SessionHandler implements SessionHandlerInterface {
  public int close ( void )
  public int destroy ( string $sessionid )
  public int gc ( int $maxlifetime )
  public int open ( string $save_path , string $sessionid )
  public string read ( string $sessionid )
  public int write ( string $sessionid , string $sessiondata )
}
session_set_save_handler(new MySessionHandler);

JsonSerializable 接口

現(xiàn)在,您可以通過實現(xiàn) JsonSerializable 接口來控制有人嘗試使用 json_encode() 對您的對象進行編碼時所發(fā)生的情況:

class Foo implements JsonSerializable {
    private $data = 'Bar';
    public function jsonSerialize() {
        return array('data'=>$this->data);
    }
}
echo json_encode(new Foo); // Outputs: {"data":"Bar"}

二進制表示法

為了與 PHP 的原生十六進制和八進制支持協(xié)調(diào)一致,現(xiàn)在也支持二進制表示法:

$mask = 0b010101;

改進了錯誤消息

錯誤消息稍有改進。

改進前:

% php -r 'class abc foo' 
Parse error: syntax error, unexpected T_STRING, expecting '{' 
in Command line code on line 1


改進后:

% php -r 'class abc foo'
Parse error: syntax error, unexpected 'foo' (T_STRING), expecting '{' 
in Command line code on line


改進可能不十分明顯,但區(qū)別是現(xiàn)在已在錯誤消息中顯示偏移標記“foo”的值。

數(shù)組到字符串轉(zhuǎn)換通知

如果您一直使用 PHP,則可能以隨機出現(xiàn)在頁面中“Array”一詞結(jié)束編程,因為您嘗試直接輸出數(shù)組。每當將數(shù)組直接轉(zhuǎn)換為字符串時,都很有可能出現(xiàn)錯誤,現(xiàn)在有了一個針對這一情況的通知:

$a = [1,2,3];
echo $a;

注意:數(shù)組到字符串轉(zhuǎn)換在 example.php onlLine 2 中

刪除的特性

最后,我們集中整理了幾年來標記為已棄用的多個特性。這些特性包括 allow_call_time_pass_reference、define_syslog_variables、highlight.bg、register_globals、register_long_arrays、magic_quotes、safe_mode、zend.ze1_compatibility_mode、session.bug_compat42、session.bug_compat_warn 以及 y2k_compliance。

除了這些特性之外,magic_quotes 可能是最大的危險。在早期版本中,未考慮因 magic_quotes 出錯導致的后果,簡單編寫且未采取任何舉措使自身免受 SQL 注入攻擊的應用程序都通過 magic_quotes 來保護。如果在升級到 PHP 5.4 時未驗證已采取正確的 SQLi 保護措施,則可能導致安全漏洞。

其他改動和特性

  • 有一種新的“可調(diào)用的”類型提示,用于某方法采用回調(diào)作為參數(shù)的情況。

  • htmlspecialchars() 和 htmlentities() 現(xiàn)在可更好地支持亞洲字符,如果未在 php.ini 文件中顯式設置 PHP default_charset,這兩個函數(shù)默認使用 UTF-8 而不是 ISO-8859-1。

  • <?=(精簡回顯語法)現(xiàn)在始終可用,無論 short_tags ini 設置的值為何。這應該使模板化系統(tǒng)創(chuàng)建者感到滿意。

  • 會話 ID 現(xiàn)在默認通過 /dev/urandom(或等效文件)中的熵生成,而不是與早期版本一樣成為必須顯式啟用的一個選項。

  • mysqlnd 這一捆綁的 MySQL 原生驅(qū)動程序庫現(xiàn)在默認用于與 MySQL 通信的各種擴展,除非在編譯時通過 ./configure 被顯式覆蓋。

可能還有 100 個小的改動和特性。從 PHP 5.3 升級到 5.4 應該極為順暢,但請閱讀遷移指南加以確保。如果您從早期版本升級,執(zhí)行的操作可能稍多一些。請查看以前的遷移指南再開始升級。

PHP 的下一步規(guī)劃是什么?

我們沒有對 PHP 進行長期規(guī)劃。PHP 將隨 Web 一起發(fā)展。我們不知道 5-10 年內(nèi)的重要 Web 趨勢和技術(shù)將是什么,但知道通過我們的不斷付出,PHP 必將存在。

短期內(nèi),我們通過“internals”郵件列表討論 PHP 開發(fā),并且就大特性達成共識時,它將發(fā)展為 RFC。您可以在 wiki.php.net/rfc 中找到 RFC。一旦我們表決同意發(fā)布一組極佳的新特性,并且對這些特性進行了正確實現(xiàn)和測試,我們便開始籌備推出新版本。

PHP 隨 Web 發(fā)展并保持穩(wěn)定的市場份額,在全球所有網(wǎng)站中,大約三分之一的網(wǎng)站都使用它。其中不僅包括一些最大的網(wǎng)站,而且還包括很大一部分最小的網(wǎng)站。我是在最小網(wǎng)站上單獨設置 PHP 的:擴展是自然而然的事情,甚至是預期的特征,也是強烈吸引工程師的特征,但縮減不太正常,并且在某些情況下更困難。如果您找到適當?shù)钠胶,并且可以將同一代碼庫用于宿舍出租乃至擁有數(shù)十億美元資產(chǎn)的公司,那么您就真正掌握了這種語言。

作者介紹

Rasmus Lerdorf 因 1995 年創(chuàng)建 PHP 項目而廣為人知,多年來,為許多其他開源項目做出了卓越貢獻。他在 Yahoo! 擔任基礎架構(gòu)師的時間已超過 7 年,并在 2012 年加入 Etsy。他出生于格陵蘭島,成長于丹麥和加拿大,并在滑鐵盧大學獲得系統(tǒng)設計工程學位。您可以在 Twitter 上關(guān)注 @rasmus。
 

關(guān)鍵詞:PHP

贊助商鏈接: