php各個版本比較

PHP的大版本主要分三支:PHP4/PHP5/PHP6 
  其中,PHP4因爲太古老、對OO支持不力已基本被淘汰,請無視PHP4。php

  PHP6因爲基本沒有生產線上的應用,還基本只是一款概念產品,不少功能已在PHP5.3.3上實現,因此也不詳述,請無視PHP6。html

  PHP5的版本主要分四支:PHP5.2以前的版本、PHP5.2.X、PHP5.3和日前發佈的PHP5.4。mysql

那咱們應該如何選擇適用本身項目的版本呢? 
PHP5.2以前的版本不值得考慮,由於某些功能缺陷或者BUG,PHP5.2以前的版本。PHP5.4還處於Beta試用的版本號,非穩定版本,請無視PHP5.4。算法

  主流PHP程序對PHP5.2.X的兼容性最好,而每次版本號的升級帶來的都是安全性和穩定性的改善,因此宜挑選最新的版本。目前PHP5.2系列最新的是PHP5.2.17。sql

  而若是產品是本身開發本身使用,PHP5.3在某些方面更具優點,在穩定性上更勝一籌,增長了不少PHP5.2所不具備的功能,好比內置php-fpm、更完善的垃圾回收算法、命名空間的引入、sqlite3的支持等等,是部署項目值得考慮的版本,強烈推薦PHP5.3.3。數據庫

  除了版本號的不一樣,同一版本號的PHP版本也有區別,而且在選擇PHP擴展的時候須要注意。json

 

下面介紹下PHP5各個版本的新功能和新特性總結: 數組

PHP5.2 之前:autoload, PDO 和 MySQLi, 類型約束
PHP5.2:JSON 支持
PHP5.3:棄用的功能,匿名函數,新增魔術方法,命名空間(後面會專門介紹),後期靜態綁定,Heredoc 和 Nowdoc, const, 三元運算符,Phar
PHP5.4:Short Open Tag, 數組簡寫形式,Traits內置 Web 服務器,細節修改
PHP5.5:yield, list() 用於 foreach, 細節修改
PHP5.6: 常量加強,可變函數參數,命名空間加強安全

1、PHP5.2之前(2006前)
順便介紹一下 PHP5.2 已經出現但值得介紹的特徵。
autoload
你們可能都知道 __autoload() 函數,若是定義了該函數,那麼當在代碼中使用一個未定義的類的時候,該函數就會被調用,你能夠在該函數中加載相應的類實現文件,如:性能優化

代碼以下:
function __autoload($classname)
{
    require_once("{$classname}.php")
}


但該函數已經不被建議使用,緣由是一個項目中僅能有一個這樣的 __autoload() 函數,由於 PHP 不容許函數重名。但當你使用一些類庫的時候,不免會出現多個 autoload 函數的須要,因而 spl_autoload_register() 取而代之:

代碼以下:
spl_autoload_register(function($classname)
{
    require_once("{$classname}.php")
});


spl_autoload_register() 會將一個函數註冊到 autoload 函數列表中,當出現未定義的類的時候,SPL [注] 會按照註冊的倒序逐個調用被註冊的 autoload 函數,這意味着你可使用 spl_autoload_register() 註冊多個 autoload 函數.
注:SPL: Standard PHP Library, 標準 PHP 庫, 被設計用來解決一些經典問題(如數據結構).

 

PDO 和 MySQLi
即 PHP Data Object, PHP 數據對象,這是 PHP 的新式數據庫訪問接口。
按照傳統的風格,訪問 MySQL 數據庫應該是這樣子:

代碼以下:
// 鏈接到服務器,選擇數據庫
$conn = mysql_connect("localhost", "user", "password");
mysql_select_db("database");

 

// 執行 SQL 查詢
$type = $_POST['type'];
$sql = "SELECT * FROM `table` WHERE `type` = {$type}";
$result = mysql_query($sql);

// 打印結果
while($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
    foreach($row as $k => $v)
        print "{$k}: {$v}\n";
}

// 釋放結果集,關閉鏈接
mysql_free_result($result);
mysql_close($conn);


爲了可以讓代碼實現數據庫無關,即一段代碼同時適用於多種數據庫(例如以上代碼僅僅適用於MySQL),PHP 官方設計了 PDO.
除此以外,PDO 還提供了更多功能,好比:

 

1.面向對象風格的接口
2.SQL預編譯(prepare), 佔位符語法
3.更高的執行效率,做爲官方推薦,有特別的性能優化
4.支持大部分SQL數據庫,更換數據庫無需改動代碼

上面的代碼用 PDO 實現將會是這樣:

代碼以下:
// 鏈接到數據庫
$conn = new PDO("mysql:host=localhost;dbname=database", "user", "password");

 

// 預編譯SQL, 綁定參數
$query = $conn->prepare("SELECT * FROM `table` WHERE `type` = :type");
$query->bindParam("type", $_POST['type']);

// 執行查詢並打印結果
foreach($query->execute() as $row)
{
    foreach($row as $k => $v)
        print "{$k}: {$v}\n";
}


PDO 是官方推薦的,更爲通用的數據庫訪問方式,若是你沒有特殊需求,那麼你最好學習和使用 PDO.
但若是你須要使用 MySQL 所特有的高級功能,那麼你可能須要嘗試一下 MySQLi, 由於 PDO 爲了可以同時在多種數據庫上使用,不會包含那些 MySQL 獨有的功能。

 

MySQLi 是 MySQL 的加強接口,同時提供面向過程和麪向對象接口,也是目前推薦的 MySQL 驅動,舊的C風格 MySQL 接口將會在從此被默認關閉。
MySQLi 的用法和以上兩段代碼相比,沒有太多新概念,在此再也不給出示例,能夠參見 PHP 官網文檔 [注]。

注:http://www.php.net/manual/en/mysqli.quickstart.php

類型約束
經過類型約束能夠限制參數的類型,不過這一機制並不完善,目前僅適用於類和 callable(可執行類型) 以及 array(數組), 不適用於 string 和 int.

代碼以下:
// 限制第一個參數爲 MyClass, 第二個參數爲可執行類型,第三個參數爲數組
function MyFunction(MyClass $a, callable $b, array $c)
{
    // ...
}

 

 

PHP5.2(2006-2011):JSON 支持

包括 json_encode(), json_decode() 等函數,JSON 算是在 Web 領域很是經常使用的數據交換格式,能夠被 JS 直接支持,JSON 其實是 JS 語法的一部分。
JSON 系列函數,能夠將 PHP 中的數組結構與 JSON 字符串進行轉換:

代碼以下:
$array = ["key" => "value", "array" => [1, 2, 3, 4]];
$json = json_encode($array);
echo "{$json}\n";

 

$object = json_decode($json);
print_r($object);


輸出:

代碼以下:
{"key":"value","array":[1,2,3,4]}
stdClass Object
(
    [key] => value
    [array] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
            [3] => 4
        )
)


值得注意的是 json_decode() 默認會返回一個對象而非數組,若是須要返回數組須要將第二個參數設置爲 true.

 

PHP5.3(2009-2012)
PHP5.3 算是一個很是大的更新,新增了大量新特徵,同時也作了一些不向下兼容的修改。
【PHP5.3棄用的功能】:如下幾個功能被棄用,若在配置文件中啓用,則 PHP 會在運行時發出警告。

Register Globals
這是 php.ini 中的一個選項(register_globals), 開啓後會將全部表單變量($_GET和$_POST)註冊爲全局變量.
看下面的例子:

代碼以下:
if(isAuth())
    $authorized = true;
if($authorized)
    include("page.php");


這段代碼在經過驗證時,將 $authorized 設置爲 true. 而後根據 $authorized 的值來決定是否顯示頁面.
但因爲並無事先把 $authorized 初始化爲 false, 當 register_globals 打開時,可能訪問 /auth.php?authorized=1 來定義該變量值,繞過身份驗證。
該特徵屬於歷史遺留問題,在 PHP4.2 中被默認關閉,在 PHP5.4 中被移除。

 

Magic Quotes

對應 php.ini 中的選項 magic_quotes_gpc, 這個特徵一樣屬於歷史遺留問題,已經在 PHP5.4 中移除。
該特徵會將全部用戶輸入進行轉義,這看上去不錯,在第一章咱們提到過要對用戶輸入進行轉義。
可是 PHP 並不知道哪些輸入會進入 SQL , 哪些輸入會進入 Shell, 哪些輸入會被顯示爲 HTML, 因此不少時候這種轉義會引發混亂。

Safe Mode
不少虛擬主機提供商使用 Safe Mode 來隔離多個用戶,但 Safe Mode 存在諸多問題,例如某些擴展並不按照 Safe Mode 來進行權限控制。
PHP官方推薦使用操做系統的機制來進行權限隔離,讓Web服務器以不一樣的用戶權限來運行PHP解釋器,請參見第一章中的最小權限原則.

【PHP5.3的新增、改進】

匿名函數
也叫閉包(Closures), 常常被用來臨時性地建立一個無名函數,用於回調函數等用途。

代碼以下:
$func = function($arg)
{
    print $arg;
};

 

$func("Hello World");


以上代碼定義了一個匿名函數,並賦值給了 $func.
能夠看到定義匿名函數依舊使用 function 關鍵字,只不過省略了函數名,直接是參數列表。
而後咱們又調用了 $func 所儲存的匿名函數。

總結:

PHP閉包的特性並無太大驚喜,其實用CLASS就能夠實現相似甚至強大得多的功能,更不能和js的閉包相提並論,只能期待PHP之後對閉包支持的改進。不過匿名函數仍是挺有用的,好比在使用preg_replace_callback等之類的函數能夠不用在外部聲明回調函數了。


匿名函數還能夠用 use 關鍵字來捕捉外部變量:

代碼以下:
function arrayPlus($array, $num)
{
    array_walk($array, function(&$v) use($num){
        $v += $num;
    });
}


上面的代碼定義了一個 arrayPlus() 函數(這不是匿名函數), 它會將一個數組($array)中的每一項,加上一個指定的數字($num).
在 arrayPlus() 的實現中,咱們使用了 array_walk() 函數,它會爲一個數組的每一項執行一個回調函數,即咱們定義的匿名函數。
在匿名函數的參數列表後,咱們用 use 關鍵字將匿名函數外的 $num 捕捉到了函數內,以便知道到底應該加上多少。

 

魔術方法:__invoke(), __callStatic()
PHP 的面向對象體系中,提供了若干「魔術方法」,用於實現相似其餘語言中的「重載」,如在訪問不存在的屬性、方法時觸發某個魔術方法。
隨着匿名函數的加入,PHP 引入了一個新的魔術方法 __invoke().
該魔術方法會在將一個對象做爲函數調用時被調用:

代碼以下:
class A
{
    public function __invoke($str)
    {
        print "A::__invoke(): {$str}";
    }
}

 

$a = new A;
$a("Hello World");


輸出毫無疑問是:

代碼以下:
A::__invoke(): Hello World


__callStatic() 則會在調用一個不存在的靜態方法時被調用。

 

命名空間
PHP的命名空間有着前無古人後無來者的無比蛋疼的語法:

代碼以下:
<?php
// 命名空間的分隔符是反斜槓,該聲明語句必須在文件第一行。
// 命名空間中能夠包含任意代碼,但只有 **類, 函數, 常量** 受命名空間影響。
namespace XXOO\Test;

 

// 該類的完整限定名是 \XXOO\Test\A , 其中第一個反斜槓表示全局命名空間。
class A{}

// 你還能夠在已經文件中定義第二個命名空間,接下來的代碼將都位於 \Other\Test2 .
namespace Other\Test2;

// 實例化來自其餘命名空間的對象:
$a = new \XXOO\Test\A;
class B{}

// 你還能夠用花括號定義第三個命名空間
namespace Other {
    // 實例化來自子命名空間的對象:
    $b = new Test2\B;

    // 導入來自其餘命名空間的名稱,並重命名,
    // 注意只能導入類,不能用於函數和常量。
    use \XXOO\Test\A as ClassA
}


更多有關命名空間的語法介紹請參見官網 [注].
命名空間時常和 autoload 一同使用,用於自動加載類實現文件:

 


spl_autoload_register(
    function ($class) {
        spl_autoload(str_replace("\\", "/", $class));
    }
);
當你實例化一個類 \XXOO\Test\A 的時候,這個類的完整限定名會被傳遞給 autoload 函數,autoload 函數將類名中的命名空間分隔符(反斜槓)替換爲斜槓,幷包含對應文件。
這樣能夠實現類定義文件分級儲存,按需自動加載。
注:http://www.php.net/manual/zh/language.namespaces.php

後期靜態綁定
PHP 的 OPP 機制,具備繼承和相似虛函數的功能,例如以下的代碼:

代碼以下:
class A
{
    public function callFuncXXOO()
    {
        print $this->funcXXOO();
    }

 

    public function funcXXOO()
    {
        return "A::funcXXOO()";
    }
}

class B extends A
{
    public function funcXXOO()
    {
        return "B::funcXXOO";
    }
}

$b = new B;
$b->callFuncXXOO();


輸出是:

代碼以下:
B::funcXXOO


能夠看到,當在 A 中使用 $this->funcXXOO() 時,體現了「虛函數」的機制,實際調用的是 B::funcXXOO().
然而若是將全部函數都改成靜態函數:

代碼以下:
class A
{
    static public function callFuncXXOO()
    {
        print self::funcXXOO();
    }

 

    static public function funcXXOO()
    {
        return "A::funcXXOO()";
    }
}

class B extends A
{
    static public function funcXXOO()
    {
        return "B::funcXXOO";
    }
}

$b = new B;
$b->callFuncXXOO();


狀況就沒這麼樂觀了,輸出是:

代碼以下:
A::funcXXOO()


這是由於 self 的語義原本就是「當前類」,因此 PHP5.3 給 static 關鍵字賦予了一個新功能:後期靜態綁定:

代碼以下:
class A
{
    static public function callFuncXXOO()
    {
        print static::funcXXOO();
    }

 

    // ...
}

// ...


這樣就會像預期同樣輸出了:

代碼以下:
B::funcXXOO

 

Heredoc 和 Nowdoc

PHP5.3 對 Heredoc 以及 Nowdoc 進行了一些改進,它們都用於在 PHP 代碼中嵌入大段字符串。
Heredoc 的行爲相似於一個雙引號字符串:

代碼以下:
$name = "MyName";
echo <<< TEXT
My name is "{$name}".
TEXT;


Heredoc 以三個左尖括號開始,後面跟一個標識符(TEXT), 直到一個一樣的頂格的標識符(不能縮進)結束。
就像雙引號字符串同樣,其中能夠嵌入變量。

 

Heredoc 還能夠用於函數參數,以及類成員初始化:

代碼以下:
var_dump(<<<EOD
Hello World
EOD
);

 

class A
{
    const xx = <<< EOD
Hello World
EOD;

    public $oo = <<< EOD
Hello World
EOD;
}


Nowdoc 的行爲像一個單引號字符串,不能在其中嵌入變量,和 Heredoc 惟一的區別就是,三個左尖括號後的標識符要以單引號括起來:

代碼以下:
$name = "MyName";
echo <<< 'TEXT'
My name is "{$name}".
TEXT;


輸出:

代碼以下:
My name is "{$name}".

 

用 const 定義常量

PHP5.3 起同時支持在全局命名空間和類中使用 const 定義常量。
舊式風格:

代碼以下:
define("XOOO", "Value");


新式風格:
const XXOO = "Value";
const 形式僅適用於常量,不適用於運行時才能求值的表達式:

代碼以下:
// 正確
const XXOO = 1234;
// 錯誤
const XXOO = 2 * 617;

 

三元運算符簡寫形式
舊式風格:

代碼以下:
echo $a ? $a : "No Value";


可簡寫成:

代碼以下:
echo $a ?: "No Value";


即若是省略三元運算符的第二個部分,會默認用第一個部分代替。

 

Phar

Phar即PHP Archive, 起初只是Pear中的一個庫而已,後來在PHP5.3被從新編寫成C擴展並內置到 PHP 中。
Phar用來將多個 .php 腳本打包(也能夠打包其餘文件)成一個 .phar 的壓縮文件(一般是ZIP格式)。
目的在於模仿 Java 的 .jar, 不對,目的是爲了讓發佈PHP應用程序更加方便。同時還提供了數字簽名驗證等功能。
.phar 文件能夠像 .php 文件同樣,被PHP引擎解釋執行,同時你還能夠寫出這樣的代碼來包含(require) .phar 中的代碼:

代碼以下:
require("xxoo.phar");
require("phar://xxoo.phar/xo/ox.php");


更多信息請參見官網 [注].
注:http://www.php.net/manual/zh/phar.using.intro.php

 

PHP5.4(2012-2013)

Short Open Tag
Short Open Tag 自 PHP5.4 起老是可用。
在這裏集中講一下有關 PHP 起止標籤的問題。即:

代碼以下:
<?php
// Code...
?>


一般就是上面的形式,除此以外還有一種簡寫形式:

代碼以下:
<? /* Code... */ ?>


還能夠把

代碼以下:
<?php echo $xxoo;?>


簡寫成:

代碼以下:
<?= $xxoo;?>


這種簡寫形式被稱爲 Short Open Tag, 在 PHP5.3 起被默認開啓,在 PHP5.4 起老是可用。
使用這種簡寫形式在 HTML 中嵌入 PHP 變量將會很是方便。

 

對於純 PHP 文件(如類實現文件), PHP 官方建議頂格寫起始標記,同時 省略 結束標記。
這樣能夠確保整個 PHP 文件都是 PHP 代碼,沒有任何輸出,不然當你包含該文件後,設置 Header 和 Cookie 時會遇到一些麻煩 [注].

注:Header 和 Cookie 必須在輸出任何內容以前被髮送。

數組簡寫形式
這是很是方便的一項特徵!

代碼以下:
// 原來的數組寫法
$arr = array("key" => "value", "key2" => "value2");
// 簡寫形式
$arr = ["key" => "value", "key2" => "value2"];

 

Traits
所謂Traits就是「構件」,是用來替代繼承的一種機制。PHP中沒法進行多重繼承,但一個類能夠包含多個Traits.

代碼以下:
// Traits不能被單獨實例化,只能被類所包含
trait SayWorld
{
    public function sayHello()
    {
        echo 'World!';
    }
}

 

class MyHelloWorld
{
    // 將SayWorld中的成員包含進來
    use SayWorld;
}

$xxoo = new MyHelloWorld();
// sayHello() 函數是來自 SayWorld 構件的
$xxoo->sayHello();


Traits還有不少神奇的功能,好比包含多個Traits, 解決衝突,修改訪問權限,爲函數設置別名等等。
Traits中也一樣能夠包含Traits. 篇幅有限不能逐個舉例,詳情參見官網 [注].
注:http://www.php.net/manual/zh/language.oop5.traits.php

 

內置 Web 服務器
PHP從5.4開始內置一個輕量級的Web服務器,不支持併發,定位是用於開發和調試環境。
在開發環境使用它的確很是方便。

代碼以下:
php -S localhost:8000


這樣就在當前目錄創建起了一個Web服務器,你能夠經過 http://localhost:8000/ 來訪問。
其中localhost是監聽的ip,8000是監聽的端口,能夠自行修改。

 

不少應用中,都會進行URL重寫,因此PHP提供了一個設置路由腳本的功能:

代碼以下:
php -S localhost:8000 index.php


這樣一來,全部的請求都會由index.php來處理。
你還可使用 XDebug 來進行斷點調試。

 

細節修改
PHP5.4 新增了動態訪問靜態方法的方式:

代碼以下:
$func = "funcXXOO";
A::{$func}();


新增在實例化時訪問類成員的特徵:

代碼以下:
(new MyClass)->xxoo();


新增支持對函數返回數組的成員訪問解析(這種寫法在以前版本是會報錯的):

代碼以下:
print func()[0];

 

PHP5.5(2013起)

yield
yield關鍵字用於當函數須要返回一個迭代器的時候, 逐個返回值。

代碼以下:
function number10()
{
    for($i = 1; $i <= 10; $i += 1)
        yield $i;
}


該函數的返回值是一個數組:

代碼以下:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


list() 用於 foreach
能夠用 list() 在 foreach 中解析嵌套的數組:

代碼以下:
$array = [
    [1, 2, 3],
    [4, 5, 6],
];

 

foreach ($array as list($a, $b, $c))
    echo "{$a} {$b} {$c}\n";


結果:

代碼以下:
1 2 3
4 5 6


細節修改
不推薦使用 mysql 函數,推薦使用 PDO 或 MySQLi, 參見前文。
再也不支持Windows XP.
可用 MyClass::class 取到一個類的完整限定名(包括命名空間)。
empty() 支持表達式做爲參數。
try-catch 結構新增 finally 塊。

 

PHP5.6

更好的常量
定義常量時容許使用以前定義的常量進行計算:

代碼以下:
const A = 2;
const B = A + 1;

 

class C
{
    const STR = "hello";
    const STR2 = self::STR + ", world";
}


容許常量做爲函數參數默認值:

代碼以下:
function func($arg = C::STR2)

 

更好的可變函數參數
用於代替 func_get_args()

代碼以下:
function add(...$args)
{
    $result = 0;
    foreach($args as $arg)
        $result += $arg;
    return $result;
}


同時能夠在調用函數時,把數組展開爲函數參數:

代碼以下:
$arr = [2, 3];
add(1, ...$arr);


// 結果爲 6
命名空間
命名空間支持常量和函數:

代碼以下:
namespace Name\Space {
    const FOO = 42;
    function f() { echo __FUNCTION__."\n"; }
}

 

namespace {
    use const Name\Space\FOO;
    use function Name\Space\f;

    echo FOO."\n";
    f();
}

PHP的大版本主要分三支:PHP4/PHP5/PHP6 
  其中,PHP4因爲太古老、對OO支持不力已基本被淘汰,請無視PHP4。

  PHP6因爲基本沒有生產線上的應用,還基本只是一款概念產品,不少功能已在PHP5.3.3上實現,因此也不詳述,請無視PHP6。

  PHP5的版本主要分四支:PHP5.2以前的版本、PHP5.2.X、PHP5.3和日前發佈的PHP5.4。

那咱們應該如何選擇適用本身項目的版本呢? 
PHP5.2以前的版本不值得考慮,由於某些功能缺陷或者BUG,PHP5.2以前的版本。PHP5.4還處於Beta試用的版本號,非穩定版本,請無視PHP5.4。

  主流PHP程序對PHP5.2.X的兼容性最好,而每次版本號的升級帶來的都是安全性和穩定性的改善,因此宜挑選最新的版本。目前PHP5.2系列最新的是PHP5.2.17。

  而若是產品是本身開發本身使用,PHP5.3在某些方面更具優點,在穩定性上更勝一籌,增長了不少PHP5.2所不具備的功能,好比內置php-fpm、更完善的垃圾回收算法、命名空間的引入、sqlite3的支持等等,是部署項目值得考慮的版本,強烈推薦PHP5.3.3。

  除了版本號的不一樣,同一版本號的PHP版本也有區別,而且在選擇PHP擴展的時候須要注意。

 

下面介紹下PHP5各個版本的新功能和新特性總結: 

PHP5.2 之前:autoload, PDO 和 MySQLi, 類型約束
PHP5.2:JSON 支持
PHP5.3:棄用的功能,匿名函數,新增魔術方法,命名空間(後面會專門介紹),後期靜態綁定,Heredoc 和 Nowdoc, const, 三元運算符,Phar
PHP5.4:Short Open Tag, 數組簡寫形式,Traits內置 Web 服務器,細節修改
PHP5.5:yield, list() 用於 foreach, 細節修改
PHP5.6: 常量加強,可變函數參數,命名空間加強

1、PHP5.2之前(2006前)
順便介紹一下 PHP5.2 已經出現但值得介紹的特徵。
autoload
你們可能都知道 __autoload() 函數,若是定義了該函數,那麼當在代碼中使用一個未定義的類的時候,該函數就會被調用,你能夠在該函數中加載相應的類實現文件,如:

代碼以下:
function __autoload($classname)
{
    require_once("{$classname}.php")
}


但該函數已經不被建議使用,緣由是一個項目中僅能有一個這樣的 __autoload() 函數,由於 PHP 不容許函數重名。但當你使用一些類庫的時候,不免會出現多個 autoload 函數的須要,因而 spl_autoload_register() 取而代之:

代碼以下:
spl_autoload_register(function($classname)
{
    require_once("{$classname}.php")
});


spl_autoload_register() 會將一個函數註冊到 autoload 函數列表中,當出現未定義的類的時候,SPL [注] 會按照註冊的倒序逐個調用被註冊的 autoload 函數,這意味着你可使用 spl_autoload_register() 註冊多個 autoload 函數.
注:SPL: Standard PHP Library, 標準 PHP 庫, 被設計用來解決一些經典問題(如數據結構).

 

PDO 和 MySQLi
即 PHP Data Object, PHP 數據對象,這是 PHP 的新式數據庫訪問接口。
按照傳統的風格,訪問 MySQL 數據庫應該是這樣子:

代碼以下:
// 鏈接到服務器,選擇數據庫
$conn = mysql_connect("localhost", "user", "password");
mysql_select_db("database");

 

// 執行 SQL 查詢
$type = $_POST['type'];
$sql = "SELECT * FROM `table` WHERE `type` = {$type}";
$result = mysql_query($sql);

// 打印結果
while($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
    foreach($row as $k => $v)
        print "{$k}: {$v}\n";
}

// 釋放結果集,關閉鏈接
mysql_free_result($result);
mysql_close($conn);


爲了可以讓代碼實現數據庫無關,即一段代碼同時適用於多種數據庫(例如以上代碼僅僅適用於MySQL),PHP 官方設計了 PDO.
除此以外,PDO 還提供了更多功能,好比:

 

1.面向對象風格的接口
2.SQL預編譯(prepare), 佔位符語法
3.更高的執行效率,做爲官方推薦,有特別的性能優化
4.支持大部分SQL數據庫,更換數據庫無需改動代碼

上面的代碼用 PDO 實現將會是這樣:

代碼以下:
// 鏈接到數據庫
$conn = new PDO("mysql:host=localhost;dbname=database", "user", "password");

 

// 預編譯SQL, 綁定參數
$query = $conn->prepare("SELECT * FROM `table` WHERE `type` = :type");
$query->bindParam("type", $_POST['type']);

// 執行查詢並打印結果
foreach($query->execute() as $row)
{
    foreach($row as $k => $v)
        print "{$k}: {$v}\n";
}


PDO 是官方推薦的,更爲通用的數據庫訪問方式,若是你沒有特殊需求,那麼你最好學習和使用 PDO.
但若是你須要使用 MySQL 所特有的高級功能,那麼你可能須要嘗試一下 MySQLi, 由於 PDO 爲了可以同時在多種數據庫上使用,不會包含那些 MySQL 獨有的功能。

 

MySQLi 是 MySQL 的加強接口,同時提供面向過程和麪向對象接口,也是目前推薦的 MySQL 驅動,舊的C風格 MySQL 接口將會在從此被默認關閉。
MySQLi 的用法和以上兩段代碼相比,沒有太多新概念,在此再也不給出示例,能夠參見 PHP 官網文檔 [注]。

注:http://www.php.net/manual/en/mysqli.quickstart.php

類型約束
經過類型約束能夠限制參數的類型,不過這一機制並不完善,目前僅適用於類和 callable(可執行類型) 以及 array(數組), 不適用於 string 和 int.

代碼以下:
// 限制第一個參數爲 MyClass, 第二個參數爲可執行類型,第三個參數爲數組
function MyFunction(MyClass $a, callable $b, array $c)
{
    // ...
}

 

 

PHP5.2(2006-2011):JSON 支持

包括 json_encode(), json_decode() 等函數,JSON 算是在 Web 領域很是經常使用的數據交換格式,能夠被 JS 直接支持,JSON 其實是 JS 語法的一部分。
JSON 系列函數,能夠將 PHP 中的數組結構與 JSON 字符串進行轉換:

代碼以下:
$array = ["key" => "value", "array" => [1, 2, 3, 4]];
$json = json_encode($array);
echo "{$json}\n";

 

$object = json_decode($json);
print_r($object);


輸出:

代碼以下:
{"key":"value","array":[1,2,3,4]}
stdClass Object
(
    [key] => value
    [array] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
            [3] => 4
        )
)


值得注意的是 json_decode() 默認會返回一個對象而非數組,若是須要返回數組須要將第二個參數設置爲 true.

 

PHP5.3(2009-2012)
PHP5.3 算是一個很是大的更新,新增了大量新特徵,同時也作了一些不向下兼容的修改。
【PHP5.3棄用的功能】:如下幾個功能被棄用,若在配置文件中啓用,則 PHP 會在運行時發出警告。

Register Globals
這是 php.ini 中的一個選項(register_globals), 開啓後會將全部表單變量($_GET和$_POST)註冊爲全局變量.
看下面的例子:

代碼以下:
if(isAuth())
    $authorized = true;
if($authorized)
    include("page.php");


這段代碼在經過驗證時,將 $authorized 設置爲 true. 而後根據 $authorized 的值來決定是否顯示頁面.
但因爲並無事先把 $authorized 初始化爲 false, 當 register_globals 打開時,可能訪問 /auth.php?authorized=1 來定義該變量值,繞過身份驗證。
該特徵屬於歷史遺留問題,在 PHP4.2 中被默認關閉,在 PHP5.4 中被移除。

 

Magic Quotes

對應 php.ini 中的選項 magic_quotes_gpc, 這個特徵一樣屬於歷史遺留問題,已經在 PHP5.4 中移除。
該特徵會將全部用戶輸入進行轉義,這看上去不錯,在第一章咱們提到過要對用戶輸入進行轉義。
可是 PHP 並不知道哪些輸入會進入 SQL , 哪些輸入會進入 Shell, 哪些輸入會被顯示爲 HTML, 因此不少時候這種轉義會引發混亂。

Safe Mode
不少虛擬主機提供商使用 Safe Mode 來隔離多個用戶,但 Safe Mode 存在諸多問題,例如某些擴展並不按照 Safe Mode 來進行權限控制。
PHP官方推薦使用操做系統的機制來進行權限隔離,讓Web服務器以不一樣的用戶權限來運行PHP解釋器,請參見第一章中的最小權限原則.

【PHP5.3的新增、改進】

匿名函數
也叫閉包(Closures), 常常被用來臨時性地建立一個無名函數,用於回調函數等用途。

代碼以下:
$func = function($arg)
{
    print $arg;
};

 

$func("Hello World");


以上代碼定義了一個匿名函數,並賦值給了 $func.
能夠看到定義匿名函數依舊使用 function 關鍵字,只不過省略了函數名,直接是參數列表。
而後咱們又調用了 $func 所儲存的匿名函數。

總結:

PHP閉包的特性並無太大驚喜,其實用CLASS就能夠實現相似甚至強大得多的功能,更不能和js的閉包相提並論,只能期待PHP之後對閉包支持的改進。不過匿名函數仍是挺有用的,好比在使用preg_replace_callback等之類的函數能夠不用在外部聲明回調函數了。


匿名函數還能夠用 use 關鍵字來捕捉外部變量:

代碼以下:
function arrayPlus($array, $num)
{
    array_walk($array, function(&$v) use($num){
        $v += $num;
    });
}


上面的代碼定義了一個 arrayPlus() 函數(這不是匿名函數), 它會將一個數組($array)中的每一項,加上一個指定的數字($num).
在 arrayPlus() 的實現中,咱們使用了 array_walk() 函數,它會爲一個數組的每一項執行一個回調函數,即咱們定義的匿名函數。
在匿名函數的參數列表後,咱們用 use 關鍵字將匿名函數外的 $num 捕捉到了函數內,以便知道到底應該加上多少。

 

魔術方法:__invoke(), __callStatic()
PHP 的面向對象體系中,提供了若干「魔術方法」,用於實現相似其餘語言中的「重載」,如在訪問不存在的屬性、方法時觸發某個魔術方法。
隨着匿名函數的加入,PHP 引入了一個新的魔術方法 __invoke().
該魔術方法會在將一個對象做爲函數調用時被調用:

代碼以下:
class A
{
    public function __invoke($str)
    {
        print "A::__invoke(): {$str}";
    }
}

 

$a = new A;
$a("Hello World");


輸出毫無疑問是:

代碼以下:
A::__invoke(): Hello World


__callStatic() 則會在調用一個不存在的靜態方法時被調用。

 

命名空間
PHP的命名空間有着前無古人後無來者的無比蛋疼的語法:

代碼以下:
<?php
// 命名空間的分隔符是反斜槓,該聲明語句必須在文件第一行。
// 命名空間中能夠包含任意代碼,但只有 **類, 函數, 常量** 受命名空間影響。
namespace XXOO\Test;

 

// 該類的完整限定名是 \XXOO\Test\A , 其中第一個反斜槓表示全局命名空間。
class A{}

// 你還能夠在已經文件中定義第二個命名空間,接下來的代碼將都位於 \Other\Test2 .
namespace Other\Test2;

// 實例化來自其餘命名空間的對象:
$a = new \XXOO\Test\A;
class B{}

// 你還能夠用花括號定義第三個命名空間
namespace Other {
    // 實例化來自子命名空間的對象:
    $b = new Test2\B;

    // 導入來自其餘命名空間的名稱,並重命名,
    // 注意只能導入類,不能用於函數和常量。
    use \XXOO\Test\A as ClassA
}


更多有關命名空間的語法介紹請參見官網 [注].
命名空間時常和 autoload 一同使用,用於自動加載類實現文件:

 


spl_autoload_register(
    function ($class) {
        spl_autoload(str_replace("\\", "/", $class));
    }
);
當你實例化一個類 \XXOO\Test\A 的時候,這個類的完整限定名會被傳遞給 autoload 函數,autoload 函數將類名中的命名空間分隔符(反斜槓)替換爲斜槓,幷包含對應文件。
這樣能夠實現類定義文件分級儲存,按需自動加載。
注:http://www.php.net/manual/zh/language.namespaces.php

後期靜態綁定
PHP 的 OPP 機制,具備繼承和相似虛函數的功能,例如以下的代碼:

代碼以下:
class A
{
    public function callFuncXXOO()
    {
        print $this->funcXXOO();
    }

 

    public function funcXXOO()
    {
        return "A::funcXXOO()";
    }
}

class B extends A
{
    public function funcXXOO()
    {
        return "B::funcXXOO";
    }
}

$b = new B;
$b->callFuncXXOO();


輸出是:

代碼以下:
B::funcXXOO


能夠看到,當在 A 中使用 $this->funcXXOO() 時,體現了「虛函數」的機制,實際調用的是 B::funcXXOO().
然而若是將全部函數都改成靜態函數:

代碼以下:
class A
{
    static public function callFuncXXOO()
    {
        print self::funcXXOO();
    }

 

    static public function funcXXOO()
    {
        return "A::funcXXOO()";
    }
}

class B extends A
{
    static public function funcXXOO()
    {
        return "B::funcXXOO";
    }
}

$b = new B;
$b->callFuncXXOO();


狀況就沒這麼樂觀了,輸出是:

代碼以下:
A::funcXXOO()


這是由於 self 的語義原本就是「當前類」,因此 PHP5.3 給 static 關鍵字賦予了一個新功能:後期靜態綁定:

代碼以下:
class A
{
    static public function callFuncXXOO()
    {
        print static::funcXXOO();
    }

 

    // ...
}

// ...


這樣就會像預期同樣輸出了:

代碼以下:
B::funcXXOO

 

Heredoc 和 Nowdoc

PHP5.3 對 Heredoc 以及 Nowdoc 進行了一些改進,它們都用於在 PHP 代碼中嵌入大段字符串。
Heredoc 的行爲相似於一個雙引號字符串:

代碼以下:
$name = "MyName";
echo <<< TEXT
My name is "{$name}".
TEXT;


Heredoc 以三個左尖括號開始,後面跟一個標識符(TEXT), 直到一個一樣的頂格的標識符(不能縮進)結束。
就像雙引號字符串同樣,其中能夠嵌入變量。

 

Heredoc 還能夠用於函數參數,以及類成員初始化:

代碼以下:
var_dump(<<<EOD
Hello World
EOD
);

 

class A
{
    const xx = <<< EOD
Hello World
EOD;

    public $oo = <<< EOD
Hello World
EOD;
}


Nowdoc 的行爲像一個單引號字符串,不能在其中嵌入變量,和 Heredoc 惟一的區別就是,三個左尖括號後的標識符要以單引號括起來:

代碼以下:
$name = "MyName";
echo <<< 'TEXT'
My name is "{$name}".
TEXT;


輸出:

代碼以下:
My name is "{$name}".

 

用 const 定義常量

PHP5.3 起同時支持在全局命名空間和類中使用 const 定義常量。
舊式風格:

代碼以下:
define("XOOO", "Value");


新式風格:
const XXOO = "Value";
const 形式僅適用於常量,不適用於運行時才能求值的表達式:

代碼以下:
// 正確
const XXOO = 1234;
// 錯誤
const XXOO = 2 * 617;

 

三元運算符簡寫形式
舊式風格:

代碼以下:
echo $a ? $a : "No Value";


可簡寫成:

代碼以下:
echo $a ?: "No Value";


即若是省略三元運算符的第二個部分,會默認用第一個部分代替。

 

Phar

Phar即PHP Archive, 起初只是Pear中的一個庫而已,後來在PHP5.3被從新編寫成C擴展並內置到 PHP 中。
Phar用來將多個 .php 腳本打包(也能夠打包其餘文件)成一個 .phar 的壓縮文件(一般是ZIP格式)。
目的在於模仿 Java 的 .jar, 不對,目的是爲了讓發佈PHP應用程序更加方便。同時還提供了數字簽名驗證等功能。
.phar 文件能夠像 .php 文件同樣,被PHP引擎解釋執行,同時你還能夠寫出這樣的代碼來包含(require) .phar 中的代碼:

代碼以下:
require("xxoo.phar");
require("phar://xxoo.phar/xo/ox.php");


更多信息請參見官網 [注].
注:http://www.php.net/manual/zh/phar.using.intro.php

 

PHP5.4(2012-2013)

Short Open Tag
Short Open Tag 自 PHP5.4 起老是可用。
在這裏集中講一下有關 PHP 起止標籤的問題。即:

代碼以下:
<?php
// Code...
?>


一般就是上面的形式,除此以外還有一種簡寫形式:

代碼以下:
<? /* Code... */ ?>


還能夠把

代碼以下:
<?php echo $xxoo;?>


簡寫成:

代碼以下:
<?= $xxoo;?>


這種簡寫形式被稱爲 Short Open Tag, 在 PHP5.3 起被默認開啓,在 PHP5.4 起老是可用。
使用這種簡寫形式在 HTML 中嵌入 PHP 變量將會很是方便。

 

對於純 PHP 文件(如類實現文件), PHP 官方建議頂格寫起始標記,同時 省略 結束標記。
這樣能夠確保整個 PHP 文件都是 PHP 代碼,沒有任何輸出,不然當你包含該文件後,設置 Header 和 Cookie 時會遇到一些麻煩 [注].

注:Header 和 Cookie 必須在輸出任何內容以前被髮送。

數組簡寫形式
這是很是方便的一項特徵!

代碼以下:
// 原來的數組寫法
$arr = array("key" => "value", "key2" => "value2");
// 簡寫形式
$arr = ["key" => "value", "key2" => "value2"];

 

Traits
所謂Traits就是「構件」,是用來替代繼承的一種機制。PHP中沒法進行多重繼承,但一個類能夠包含多個Traits.

代碼以下:
// Traits不能被單獨實例化,只能被類所包含
trait SayWorld
{
    public function sayHello()
    {
        echo 'World!';
    }
}

 

class MyHelloWorld
{
    // 將SayWorld中的成員包含進來
    use SayWorld;
}

$xxoo = new MyHelloWorld();
// sayHello() 函數是來自 SayWorld 構件的
$xxoo->sayHello();


Traits還有不少神奇的功能,好比包含多個Traits, 解決衝突,修改訪問權限,爲函數設置別名等等。
Traits中也一樣能夠包含Traits. 篇幅有限不能逐個舉例,詳情參見官網 [注].
注:http://www.php.net/manual/zh/language.oop5.traits.php

 

內置 Web 服務器
PHP從5.4開始內置一個輕量級的Web服務器,不支持併發,定位是用於開發和調試環境。
在開發環境使用它的確很是方便。

代碼以下:
php -S localhost:8000


這樣就在當前目錄創建起了一個Web服務器,你能夠經過 http://localhost:8000/ 來訪問。
其中localhost是監聽的ip,8000是監聽的端口,能夠自行修改。

 

不少應用中,都會進行URL重寫,因此PHP提供了一個設置路由腳本的功能:

代碼以下:
php -S localhost:8000 index.php


這樣一來,全部的請求都會由index.php來處理。
你還可使用 XDebug 來進行斷點調試。

 

細節修改
PHP5.4 新增了動態訪問靜態方法的方式:

代碼以下:
$func = "funcXXOO";
A::{$func}();


新增在實例化時訪問類成員的特徵:

代碼以下:
(new MyClass)->xxoo();


新增支持對函數返回數組的成員訪問解析(這種寫法在以前版本是會報錯的):

代碼以下:
print func()[0];

 

PHP5.5(2013起)

yield
yield關鍵字用於當函數須要返回一個迭代器的時候, 逐個返回值。

代碼以下:
function number10()
{
    for($i = 1; $i <= 10; $i += 1)
        yield $i;
}


該函數的返回值是一個數組:

代碼以下:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


list() 用於 foreach
能夠用 list() 在 foreach 中解析嵌套的數組:

代碼以下:
$array = [
    [1, 2, 3],
    [4, 5, 6],
];

 

foreach ($array as list($a, $b, $c))
    echo "{$a} {$b} {$c}\n";


結果:

代碼以下:
1 2 3
4 5 6


細節修改
不推薦使用 mysql 函數,推薦使用 PDO 或 MySQLi, 參見前文。
再也不支持Windows XP.
可用 MyClass::class 取到一個類的完整限定名(包括命名空間)。
empty() 支持表達式做爲參數。
try-catch 結構新增 finally 塊。

 

PHP5.6

更好的常量
定義常量時容許使用以前定義的常量進行計算:

代碼以下:
const A = 2;
const B = A + 1;

 

class C
{
    const STR = "hello";
    const STR2 = self::STR + ", world";
}


容許常量做爲函數參數默認值:

代碼以下:
function func($arg = C::STR2)

 

更好的可變函數參數
用於代替 func_get_args()

代碼以下:
function add(...$args)
{
    $result = 0;
    foreach($args as $arg)
        $result += $arg;
    return $result;
}


同時能夠在調用函數時,把數組展開爲函數參數:

代碼以下:
$arr = [2, 3];
add(1, ...$arr);


// 結果爲 6
命名空間
命名空間支持常量和函數:

代碼以下:
namespace Name\Space {
    const FOO = 42;
    function f() { echo __FUNCTION__."\n"; }
}

 

namespace {
    use const Name\Space\FOO;
    use function Name\Space\f;

    echo FOO."\n";    f();}

相關文章
相關標籤/搜索