学习背景
在看 laravel 源码的时候,有很多的匿名函数。对于小白,这样看的我头疼。不过,多看几遍之后,慢慢也就看懂了,为了加深印象,就想深入学习下匿名函数。
参考文章
链接:www.cnblogs.com/echojson/p/…
PHP Closure(闭包)类详解
Closure
闭包是为了函数的复用,PHP 里面闭包函数是为了复用函数而设计的语言特性,如果在闭包函数里面访问指定域的变量,使用 use 关键字来实现。PHP 具有面向函数的编程特性,也是面向对象编程语言。PHP 会自动把闭包函数转换成内置类 Closure 的对象实例,依赖 Closure 的对象实例又给闭包函数添加更多能力。闭包不能被实例化(私有构造函数),也不能被继承。可以通过反射来判读闭包实例是否能被实例和继承!
$say = function () {
echo "Say";
};
var_dump($say);
//下面是打印结果
//PHP会自动把闭包函数转换成内置类 Closure 的对象实例
object(Closure)#1 (0) {
}
匿名函数
$say = function() {
echo "Say";
}
// 1:匿名函数的调用
$say();
test(Closore $callback) {
return $callback();
}
// 2:第二种调用
test($say);
实现闭包
将匿名函数在普通函数中当做参数传入,也可以在函数中被返回。 连接闭包和外界变量的关键字:USE
$helloWorld = function () {
$hello = "Hello";
// use 引用的变量也只不过是一个变量的副本 CLONE 而已(非完全引用变量本身),因此如果要对变量有修改就必须在变量之前加上一个 & 符号。
$worlds = function () use(&$hello) {
$hello .= " World";
};
$worlds();
echo $hello;
};
$helloWorld();
var_dump($helloWorld);
类里面的匿名函数
class A {
public static function testA() {
return function ($i) {
return $i+100;
};
}
}
function B(Closure $callback) {
return $callback(200);
}
// testA返回的就是一个匿名的函数
$a = B(A::testA());
print_r($a);
匿名函数的绑定
匿名函数的访问不在是匿名函数范围,而是一个类的访问范围,那么我们就将”一个匿名函数绑定到一个类中”
Closure {
__construct ( void )
public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])
public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}
参数说明
closure:需要绑定的匿名函数 newthis:需要绑定到匿名函数的对象,或者NULL创建未绑定的闭包 newscope:想要绑定给闭包的类作用域,或者 ‘static’ 标识 不改变。如果传入一个对象,则使用这个对象的类别名。类作用域来决定在闭包中 $this 对象的私有、保护方法的可见性。可以传入类名或者类的实例,默认值是 ‘static’ ,表示不改变。 返回值:返回一个新的 Closure 对象,或者在失败时返回 false。
Closure::bind 和 Closure::bindTo的静态版本
深入理解bind参数,要理解bind参数,先要理解类的属性的访问权限 public、protect(子类可以访问)、private(子类可以访问,类外不可以访问)。 public:公有类型,在子类中可以通过 self::obj->var 来调用 public 类型的方法或是属性。 protect:受保护类型在子类中可以通过 self::obj->var来调用protect类型的方法和属性。 private:私有类型的属性或是方法只能在该类中使用,在该类实例、子类、子类中的实例都能调用私有属性和方法。
不能通过 this 。原因:静态属性属于类本身而不属于类的任何实例。静态属性可以被看作是存储在类中的全局变量,可以在任何地方通过类访问它们。在类外不能通过 类名::私有静态变量,只能在类里通过 self或者 static 访问私有静态变量。
第一个参数:需要绑定的匿名函数。 第二个参数:关闭bind的第二参数为 object 还是 null,取决于第一个参数闭包中是否用发哦 ·this 的取值)若闭包中用到 $this 则第二个参数不能为 null,只能为 object实例对象;若闭包用到静态访问(::),第二个参数就可以为null,也可以为 object。 第三个参数:作用域,是控制闭包的作用域,如果闭包访问的private属性,就需要第三个参数提升闭包的访问权限,若闭包访问的public属性,第三个参数可以不用。只不要改变访问权限时才需要,传对象或者类名都行。
<?php
class A {
private $name = '猫王';
protected $age = 30;
private static $weight = '70kg';
public $address = '中国';
public static $height = '180cm';
}
$obj = new A();
// 报错 私有属性,不能实例话访问
//$obj->name;
// 共有属性,不能实例化访问
//$obj->age;
// 私有静态方法,不能类名访问
//A::$weight;
$obj->address;
A::$height;
$fun = function () {
// 单独的代码是不能运行 $this 的,程序根本不知道我要调用哪个对象,即使是知道是哪个对象是否拥有这个属性,
// 没有这个属性也同样会报错
return $this->name;
};
// 报错 Fatal error: Uncaught Error: Using $this when not in object context
//echo $fun();
$fun = function () {
return A::$height;
};
//echo $fun();
$fun = function () {
return $this->name;
};
// 第一种情况:bind 的第二个参数为 null
// 报错,因为bind的第二个参数为 null,所以 $fun 函数中的 $this 不知道是哪个 $this,因为会报错。
// Fatal error: Uncaught Error: Using $this when not in object context
// 第二种情况:
// 第二个参数指定的类,第三个参数为 null,同样会报错。
// 报错原因是 A类中 name属性数私有方法,不能访问。
// Uncaught Error: Cannot access private property A::$name
// 注意 当匿名函数中用到 $this ,bind的第二个参数一定是 为该类,不然会报错、
$name = Closure::bind($fun,new A(),new A());
//echo $name();
$fun = function () {
return self::$weight;
};
$wight = Closure::bind($fun,null,A::class);
echo $wight();
//综上大家应该理解其用法了吧,有时第二个参数为null,有时第三个参数可以不要,这些都跟你匿名函数里 代码中访问的方式紧密相关
//总结:
//1、一般匿名函数中有$this->name类似这样用 $this访问属性方式时,你在使用bind绑定时 ,第二个参数肯定要写,写出你绑定那个对象实例,第三个参数要不要呢,要看你访问的这个属性,在绑定对象中的权限属性,如果是private,protected 你要使用第三个参数 使其变为公有属性, 如果本来就是公有,你可以省略,也可以不省略
//2、一般匿名函数中是 类名::静态属性 类似这样的访问方式(比如例子中A::$weight),你在使用bind绑定时,第二个参数可以写null,也可以写出具体的对象实例,一般写null就行(写了具体对象实例多此一举),第三个参数写不写还是得看你访问的这个静态属性的权限是 private 还是 public,如果是私有private或受保护protected的,你就得第三个参数必须写,才能使其权限变为公有属性 正常访问,如果本来就是公有public可以不用写,可以省略
//
//3、需要提升属性作用域时,第3个参数需要传,传对象或者类名都可以。