面向對象變成語言代碼的複用主要採用繼承來實現,而函數的複用,就是經過閉包來實現。這就是閉包的設計初衷。php
注:PHP裏面閉包函數是爲了複用函數而設計的語言特性,若是在閉包函數裏面訪問指定域的變量,使用use關鍵字來實現。編程
PHP具備面向函數的編程特性,可是也是面向對象編程語言,PHP 會自動把閉包函數轉換成內置類 Closure 的對象實例,依賴Closure 的對象實例又給閉包函數添加了更多的能力。 閉包
閉包不能被實例(私有構造函數),也不能被繼承(finally 類)。能夠經過反射來判斷閉包實例是否能被實例,繼承。編程語言
提到閉包就不得不想起匿名函數,也叫閉包函數(closures),貌似PHP閉包實現主要就是靠它。聲明一個匿名函數是這樣:函數
$func = function() { }; //帶結束符
能夠看到,匿名函數由於沒有名字,若是要使用它,須要將其返回給一個變量。匿名函數也像普通函數同樣能夠聲明參數,調用方法也相同:this
$func = function( $param ) { echo $param; }; $func( 'some string' ); //輸出: //some string
順便提一下,PHP在引入閉包以前,也有一個能夠建立匿名函數的函數:create function,可是代碼邏輯只能寫成字符串,這樣看起來很晦澀而且很差維護,因此不多有人用。spa
將匿名函數在普通函數中當作參數傳入,也能夠被返回。這就實現了一個簡單的閉包。設計
鏈接閉包和外界變量的關鍵字:USEcode
PHP在默認狀況下,匿名函數不能調用所在代碼塊的上下文變量,而須要經過使用use關鍵字。對象
function getMoney() { $rmb = 1; $func = function() use ( $rmb ) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; //閉包內的變量改變了,可是閉包外沒有改變。 } getMoney(); //輸出: //1 //1
注:use所引用的是變量的複製(副本而),並非徹底引用變量。若是要達到引用的效果,就須要使用 & 符號,進行引用傳遞參數。
function getMoney() { $rmb = 1; $func = function() use ( &$rmb ) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney(); //輸出: //1 //2
閉包函數不能直接訪問閉包外的變量,而是經過use 關鍵字來調用上下文變量(閉包外的變量),也就是說經過use來引用上下文的變量;
閉包內所引用的變量不能被外部所訪問(即,內部對變量的修改,外部不受影響),若想要在閉包內對變量的改變從而影響到上下文變量的值,須要使用&的引用傳參。
PHP Closure 類是用於表明匿名函數的類,匿名函數(在 PHP 5.3 中被引入)會產生這個類型的對象,Closure類摘要以下:
Closure { __construct ( void ) public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ]) public Closure bindTo (object $newthis [, mixed $newscope = 'static' ]) }
方法說明:
Closure::__construct — 用於禁止實例化的構造函數 Closure::bind — 複製一個閉包,綁定指定的$this對象和類做用域。 Closure::bindTo — 複製當前閉包對象,綁定指定的$this對象和類做用域。
除了此處列出的方法,還有一個 __invoke 方法。這是爲了與其餘實現了 __invoke()魔術方法 的對象保持一致性,但調用閉包對象的過程與它無關。
參數說明:
closure表示須要綁定的閉包對象。
newthis表示須要綁定到閉包對象的對象,或者NULL建立未綁定的閉包。
newscope表示想要綁定給閉包的類做用域,能夠傳入類名或類的示例,默認值是 'static', 表示不改變。
返回值:成功時返回一個新的 Closure 對象,失敗時返回FALSE。
Closure::bind是Closure::bindTo的靜態版本
例子:
class Animal { public $cat = 'cat'; public static $dog = 'dog'; private $pig = 'pig'; private static $duck = 'duck'; } //不能經過 $this 訪問靜態變量 //不一樣經過 類名::私有靜態變量,只能經過self,或者static,在類裏面訪問私有靜態變量 $cat = function() { return $this->cat; }; $dog = static function () { return Animal::$dog; }; $pig = function() { return $this->pig; }; $duck = static function() { //return Animal::$duck; 這樣寫,會報錯,提示不能經過類名訪問私有靜態變量 return self::$duck; // return static::$duck }; $bindCat = Closure::bind($cat, new Animal(), 'Animal'); $bindCat2 = Closure::bind($cat, new Animal(), new Animal()); echo $bindCat() . PHP_EOL; echo $bindCat2() . PHP_EOL; $bindDog = Closure::bind($dog, null, 'Animal'); $bindDog2 = Closure::bind($dog, null, new Animal()); echo $bindDog() . PHP_EOL; echo $bindDog2() . PHP_EOL; $bindPig = Closure::bind($pig, new Animal(), 'Animal'); $bindPig2 = Closure::bind($pig, new Animal(), new Animal()); echo $bindPig() . PHP_EOL; echo $bindPig2() . PHP_EOL; $bindDuck = Closure::bind($duck, null, 'Animal'); $bindDuck2 = Closure::bind($duck, null, new Animal()); echo $bindDuck() . PHP_EOL; echo $bindDuck2() . PHP_EOL;
經過上面的例子,能夠看出函數複用得,能夠把函數掛在不一樣的類上,或者對象上。
總結:
1. 閉包內若是用 $this, 則 $this 只能調用非靜態的屬性,這和實際類中調用原則是一致的,且 Closure::bind() 方法的第2個參數不能爲null,必須是一個實例 (由於$this,必須在實例中使用),第三個參數能夠是實例,能夠是類字符串,或 static;
2. 閉包內調用靜態屬性時,閉包必須聲明爲 static,同時Closure::bind()方法的第2個參數須要爲null,由於 靜態屬性不須要實例,第3個參數能夠是類字符串,實例,staic.