PHP>5.3版本部分新功能

航空母艦發表於2016-02-26

【PHP5.3的新增、改進】匿名函式
也叫閉包(Closures), 經常被用來臨時性地建立一個無名函式,用於回撥函式等用途。

$func = function ($arg) {
    print $arg;
};
$func("Hello World");
<?php
$f = function () {
    return 100;
};

function testClosure(Closure $callback) {
    return $callback();
}

$a = testClosure($f);
print_r($a); //100
exit;
如果要呼叫一個類裡面的匿名函式呢
<?php

class C
{
    public static function testC() {
        return function ($i) {
            return $i + 100;
        };
    }
}


function testClosure(Closure $callback) {
    return $callback(13);
}

$a = testClosure(C::testC());
print_r($a);exit;
其中的C::testC()返回的是一個funciton,我們就需要將“一個匿名函式繫結到一個類中”。
<?php
class A{
    public $base = 100;
}
$f = function () {
    return $this->base + 3;
};

$a = Closure::bind($f, new A);
print_r($a());
f這個匿名函式中莫名奇妙的有個this,這個this關鍵詞就是說明這個匿名函式是需要繫結在類中的。繫結之後,就好像A中有這麼個函式一樣,但是這個函式是public還是private,bind的最後一個引數就說明了這個函式的可呼叫範圍
匿名函式還可以用 use 關鍵字來捕捉外部變數:
function arrayPlus($array, $num)
{
    array_walk($array, function (&$v) use ($num) {
        $v += $num;
    });
}
上面的程式碼定義了一個 arrayPlus() 函式(這不是匿名函式), 它會將一個陣列($array)中的每一項,加上一個指定的數字($num).在 arrayPlus() 的實現中,我們使用了 array_walk() 函式,它會為一個陣列的每一項執行一個回撥函式,即我們定義的匿名函式。在匿名函式的引數列表後,我們用 use 關鍵字將匿名函式外的 $num 捕捉到了函式內,以便知道到底應該加上多少。
PHP引數型別約束,不論是介面,抽象類,函式,方法,在5.3+以上版本都可以使用,不過目前只能宣告 array,object ,可執行型別(callable.Closure)這3種.ConnectionInterface就是屬於object
public function addConnection($name, ConnectionInterface $connection)
{
    $this->connections[$name] = $connection;
}
 
後期靜態繫結PHP的繼承模型中有一個存在已久的問題,那就是在父類中引用擴充套件類的最終狀態比較困難。
<?php
class ParentBase
{
    static $property = 'Parent Value';

    public static function render()
    {
        return self::$property;
    }

}

class Descendant extends ParentBase
{
    static $property = 'Descendant Value';
}

//output: Parent Value
echo Descendant::render();
在這個例子中,render()方法中使用了self關鍵字,這是指ParentBase類而不是指Descendant類。在 ParentBase::render()方法中沒法訪問$property的最終值。為了解決這個問題,需要在子類中重寫render()方法。通過引入延遲靜態繫結功能,可以使用static作用域關鍵字訪問類的屬性或者方法的最終值
<?php

class ParentBase
{
    static $property = 'Parent Value';

    public static function render()
    {
        return static::$property;
    }
}

class Descendant extends ParentBase
{
    static $property = 'Descendant Value';
}

//output: Descendant Value
echo Descendant::render();
PHP的物件導向體系中,提供了若干“魔術方法”,用於實現類似其他語言中的“過載”,如在訪問不存在的屬性、方法時觸發某個魔術方法。

__call($funcname, $arguments)

__callStatic($funcname, $arguments)

引數說明:

$funcname String 呼叫的不存在的方法名稱。

$arguments Array 呼叫方法時所帶的引數。
 __invoke魔術方法會在將一個物件作為函式呼叫時被呼叫:

class A
{
    public function __invoke($args)
    {
        print "A::__invoke(): {$args}";
    }
}

$a = new A;
//output: A::__invoke(): Hello World
$a("Hello World");
 __callStatic則會在呼叫一個不存在的靜態方法時被呼叫,有了__callStatic,可以省不少程式碼了。而且這個方法支援在子類中呼叫,配合上get_called_class,子類也一起魔術了
<?php

class ActiveRecordBase
{
    /**  As of PHP 5.3.0  */
    public static function __callStatic($func, $arguments)
    {
        if ($func == 'getById') {
            $id = $arguments[0];
            return get_called_class() . '(' . $id . ')';
        }

        throw new Exception('Invalid method : ' . $name);
    }
}

class Person extends ActiveRecordBase
{

}

// output: Person(123)
echo Person::getById(123);

__call 當要呼叫的方法不存在或許可權不足時,會自動呼叫__call 方法。

<?php

class Db
{
    private $sql = array(
        "field" => "",
        "where" => "",
        "order" => "",
        "limit" => "",
        "group" => "",
        "having" => "",
    );

    // 連貫操作呼叫field() where() order() limit() group() having()方法,組合sql語句
    function __call($methodName, $args)
    {
        // 將第一個引數(代表不存在方法的方法名稱),全部轉成小寫方式,獲取方法名稱
        $methodName = strtolower($methodName);

        // 如果呼叫的方法名和成員屬性陣列$sql下標對應上,則將第二個引數給陣列中下標對應的元素
        if (array_key_exists($methodName, $this->sql)) {
            $this->sql[$methodName] = $args[0];
        } else {
            echo '呼叫類' . get_class($this) . '中的方法' . $methodName . '()不存在';
        }
        // 返回自己物件,則可以繼續呼叫本物件中的方法,形成連貫操作
        return $this;
    }

    // 輸出連貫操作後組合的一個sql語句,是連貫操作最後的一個方法
    function select()
    {
        echo "SELECT {$this->sql['field']} FROM  user {$this->sql['where']} {$this->sql['order']} {$this->sql['limit']} {$this->sql['group']}
                {$this->sql['having']}";
    }
}

$db = new Db();

// 連貫操作
$db->field('sex, count(sex)')
    ->where('where sex in ("男","女")')
    ->group('group by sex')
    ->having('having avg(age) > 25')
    ->select();
?>

名稱空間一個最明確的目的就是解決重名問題,PHP中不允許兩個函式或者類出現相同的名字,否則會產生一個致命的錯誤。這種情況下只要避免命名重複就可以解決,最常見的一種做法是約定一個字首。

<?php
//建立空間Blog
namespace Blog;

class Comment
{
}

//非限定名稱,表示當前Blog空間
//這個呼叫將被解析成 Blog\Comment();
$blog_comment = new Comment();

//限定名稱,表示相對於Blog空間
//這個呼叫將被解析成 Blog\Article\Comment();
$article_comment = new Article\Comment(); //類前面沒有反斜杆\

//完全限定名稱,表示絕對於Blog空間
//這個呼叫將被解析成 Blog\Comment();
$article_comment = new \Blog\Comment(); //類前面有反斜杆\

//完全限定名稱,表示絕對於Blog空間
//這個呼叫將被解析成 Blog\Article\Comment();
$article_comment = new \Blog\Article\Comment(); //類前面有反斜杆\


//建立Blog的子空間Article
namespace Blog\Article;

class Comment
{
}
?>
別名和匯入可以看作是呼叫名稱空間元素的一種快捷方式。PHP並不支援匯入函式或常量。 它們都是通過使用use操作符來實現:
<?php
namespace Blog\Article;

class Comment
{
}

//建立一個BBS空間(我有打算開個論壇)
namespace BBS;

//匯入一個名稱空間
use Blog\Article;

//匯入名稱空間後可使用限定名稱呼叫元素
$article_comment = new Article\Comment();

//為名稱空間使用別名
use Blog\Article as Arte;

//使用別名代替空間名
$article_comment = new Arte\Comment();

//匯入一個類
use Blog\Article\Comment;

//匯入類後可使用非限定名稱呼叫元素
$article_comment = new Comment();

//為類使用別名
use Blog\Article\Comment as Comt;

//使用別名代替空間名
$article_comment = new Comt();
?>
php內建的類,不隸屬於任何名稱空間如果你需要在名稱空間中使用須有 \ 宣告
new \DateTime();
PHP5.4

陣列簡寫形式

<?php
//原來的陣列寫法
$arr = array("key" => "value", "key2" => "value2");
// 簡寫形式
$arr = ["key" => "value", "key2" => "value2"];
?>
Traits
所謂Traits就是“構件”,是用來替代繼承的一種機制。Trait和類相似,但不能被例項化PHP中無法進行多重繼承,但一個類可以包含多個Traits.
<?php
trait SayWorld
{
    public $var = 'test';
    public function sayHello()
    {
        echo 'World!';
    }
}

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

$xx = new MyHelloWorld();
// sayHello()函式是來自SayWorld構件的 $xx->var
$xx->sayHello();
Traits還有很多神奇的功能,比如包含多個Traits, 解決衝突,修改訪問許可權,為函式設定別名等等。
<?php

class A
{
    public function callFuncTest() {
        print $this->funcTest();
    }

    public function funcTest() {
        return "this is A::funcTest().<br/>";
    }
}
$func = "funcTest";
echo A::{$func}();
echo (new A)->funcTest();
新增在例項化時訪問類成員的特徵
(new MyClass)->xx();
 新增支援對函式返回陣列的成員訪問解析
print [1, 2, 3][0];
 PHP5.5
yield的一個功能就是能有效的降低迭代的記憶體開銷,yield關鍵字用於當函式需要返回一個迭代器的時候, 逐個返回值.也就是說, 每當產生一個陣列元素, 就通過yield關鍵字返回成一個, 並且函式執行暫停, 當返回的迭代器的next方法被呼叫的時候, 會恢復剛才函式的執行, 使用上yield實現,裡面所有的中間變數都只使用一個記憶體$i,這樣節省的時間和空間都會變小.從上一次被yield暫停的位置開始繼續執行, 到下一次遇到yield的時候, 再次返回.
<?php
function generators()
{
    for ($i = 1; $i <= 10; $i += 1) {
        yield $i;
    }

}

foreach (generators() as $v) {
    echo $v;
}
 可以用 list() 在 foreach 中解析巢狀的陣列:
$array = [
    [1, 2, 3],
    [4, 5, 6],
];

foreach ($array as list($a, $b, $c))
    echo "{$a} {$b} {$c}\n";
 類名通過::class可以獲取
<?php
namespace Name\Space;
class ClassName {}
 
echo ClassName::class;
 
echo "\n";
?>
 PHP7函式返回值型別宣告(注意 … 的邊長引數語法在 PHP 5.6 以上的版本中才有)
<?php

namespace DesignPatterns\Creational\AbstractFactory;

class JsonFactory extends AbstractFactory
{
    public function createText(string $content): Text
    {
        return new JsonText($content);
    }
}
 return的結果必須是Text物件
<?php
function arraysSum(array ...$arrays): array 
{ 
    return array_map(function(array $array): int { 
        return array_sum($array); 
    }, $arrays); 
} 

print_r(arraysSum([1,2,3], [4,5,6], [7,8,9])); 

相關文章