面向对象变成语言代码的复用主要采用继承来实现,而函数的复用,就是经过闭包来实现。这就是闭包的设计初衷。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.