【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]));