最近在看laravel底层代码时 ,发现代码中很多Closure::bind用法,查询各种资料,一直对它的使用一直半解(网上大多都是抄袭教材,解释的不清楚),还好经过我不懈努力,终于弄懂了其中道理。
1、在正式解释前我们先了解一些基础的东西(稍安勿躁,磨刀不误砍柴工。大多说看不懂其用法的人,主要还是基础不牢)。php中 闭包我们也可以叫做匿名函数,匿名函数不了解的可以自行补一下,我这边简单举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$say = function(){ return '我是匿名函数'; }; echo $say(); //这是最直接调用匿名函数方式 输出 我是匿名函数 function test(Closure $callback){ return $callback(); } echo test($say); //这是间接调用匿名函数方式 同样输出 我是匿名函数 //当然也可以这样写 echo test( function(){ return '我是匿名函数'; }); |
2、接着我们来说一下php的public、protected、private三种访问控制模式的区别
public: 公有类型,在子类中可以通过self::var调用public方法或属性,parent::method调用父类方法
在实例中可以通过$obj->var 来调用 public类型的方法或属性
protected: 受保护类型,在子类中可以通过self::var调用protected方法或属性,parent::method调用父类方法。
在实例中不能通过$obj->var 来调用 protected类型的方法或属性
private: 私有类型,该类型的属性或方法只能在该类中使用,在该类的子类中都不能调用私有类型的属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php class A { private $name = '王力宏'; protected $age = '30'; private static $weight = '70kg'; public $address = '中国'; public static $height = '180cm'; } $obj = new A(); echo $obj->name;//报错 Cannot access private property A::$name echo $obj->age;//报错 Cannot access protected property A::$age echo A::$weight; //报错 Cannot access private property A::$weight echo $obj->address;//正常 输出 中国 echo A::$height;//正常 输出 180cm $fun = function(){ $obj = new A(); return $obj->address;//实例对象可以获得公有属性, $obj->name等私有属性肯定不行 上面例子已列出报错 } echo $fun();//正常 输出 中国 $fun2 = function(){ return A::$height;// 类可以直接访问公有静态属性,但A::$weight肯定不行,因为weight为私有属性 } echo $fun2();//正常 输出 180cm |
3、在以上都理解的情况下 我们来看看这样的情况,有如下匿名函数:
1 2 3 4 5 6 7 8 |
$fun = function(){ return $this->name; } 或者 $fun = function(){ return A::$height; } echo $fun();//会报错的 |
其实单独这段代码是肯定不能运行的,因为里面有个$this,程序压根不知道你这个$this是代表哪个对象或哪个类,因此想让其正常运行肯定有前提条件啊(就好比你想遍历某个数组一样,如果这个数组压根你就没提前定义声明,那么肯定会报错的)
如果这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php class A { private $name = '王力宏'; protected $age = '30'; private static $weight = '70kg'; public $address = '中国'; public static $height = '180cm'; } $fun = function(){ return $this->name; }; $cf = Closure::bind($fun,new A() ,'A'); //该函数返回一个新的 Closure 对象 或者在失败时返回 FALSE echo $cf();//输出 王力宏 为什么呢:bind第一个参数是匿名函数,第二个A实例对象,第三个代表作用域 //网上很多讲解是说把匿名函数绑定到了实例对象上了 然后就怎么着......很多新手看了依然一脸懵逼。其实是匿名函数里的$this被指定到了或绑定到了A实例对象上了 Closure::bind($fun,new A() );这个使用 你可以理解成对匿名函数做了如下过程: $fun = function(){ $this = new A();//你可以想象认为,$this就成了A类的实例对象了,然后在去访问name属性,就和我们正常实例化类访问成员属性一样,上面2中的例子$obj = new A()就是这样,(因为$this是关键字,在这里我们其实不能直接$this = new A();这么写,为了好理解我写成$this,但是原理还是这个意思),但是我们都知道因为name属性是私有的,上面2中我已说过,实例对象不能访问私有属性,那该怎么办呢,于是添加第三个参数就很重要了,一般传入传入一个对应对象,或对应类名(对应的意思是:匿名函数中$this-name想获取name属性值,你这个$this想和那个类和对象绑定在一起呢,就是第二个参数,这时你第三个参数写和第二个参数写一样的对象或类就行了,就是作用域为这个对象或类,这就会让原理的name私有属性变为公有属性) return $this->name; } ; |
为了加深理解我在举两个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php class A { private $name = '王力宏'; protected $age = '30'; private static $weight = '70kg'; public $address = '中国'; public static $height = '180cm'; } $fun = function(){ $obj = new A(); return $obj->address; }; echo $fun();//正常 输出 中国 ,该例子其实在上面2中已经讲过了 如果跟这个还不能理解 就不要往下看了 好好补一下基础 $fun2 = function(){ return $this->address; }; //echo $fun2();这样运行会报错 所以我们要进行匿名函数的绑定 是匿名函数里的$this有所指,**必要时还要改变要访问属性的作用域** $newfun2 = Closure::bind($fun2, new A() );//使用了该函数后 ,该函数返回一个全新的匿名的函数,和原来匿名函数$fun2一模一样,只是其中 $this被指向了A实例对象,这样就能访问address属性了 echo $newfun2();//正常 输出 中国,在这里数一下bind这次为什么没添加第三个参数,因为我们要访问的address属性是公有的,一个对象实例是可以直接访问公有属性的,这个例子中只要匿名函数中$this被指向了A对象实例(或者叫绑定也可以),就能访问到公有属性,所以可以不用添加第三个参数,当然你加上了第三个参数 如这样Closure::bind($fun2, new A(), ‘A’ );或Closure::bind($fun2, new A(), nwe A() );不影响 照样运行,就好比把原来公有属性 变为公有属性 不影响的(一般当我们访问的属性为私有属性时,才使用第三个参数改变作用域 ,使其变为公有属性) |
下面来看最后一例子,再次加深一下理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php class A { private $name = '王力宏'; protected $age = '30'; private static $weight = '70kg'; public $address = '中国'; public static $height = '180cm'; } $fun = function(){ return A::$weight; }; //echo $fun(); 运行会报错 因为weight为私有属性 上面2中例子访问height属性是可以的,height为公有属性,所以把weight改成height是可以正常运行的,但是我们现在就想访问这个私有静态属性,我们该怎么办,于是Closure::bind出场了 $newfun = Closure::bind($fun,null,'A');//通过bind函数作用,返回一个和$fun匿名函数一模一样的匿名函数,只是该匿名函数中A::$weight, weight属性由私有变成公有属性了。 echo $newfun();// 正常 输出 70kg ,为什么第二个参数又成null了呢,因为在该匿名函数中A::$weight 这属于正常类使用啊(php中 类名::公有静态属性,这是正常访问方法,上面2中例子已经说的很清楚了),所以不用绑定到某个对象上去了,于是第二个参数可以省略,唯一遗憾的是weight属性虽是静态属性,但是其权限是private私有属性,于是我们要把私有属性变公有属性就可以了,这时把第三个参数加上去就可以了,第三个参数可以是A类(Closure::bind($fun,null,'A')),也可以是A类的对象实例(Closure::bind($fun,null, new A() )),两种写法都可以,最终第三个参数的添加使私有属性变成了公有属性,(这个例子中当然你非得添加第二个参数肯定也没问题,只要第二个参数是A的实例对象就行Closure::bind($fun,new A(),'A'),不影响,只是说 A::$weight 这种使用方法本身就是正常使用,程序本身就知道你用的是A类,你在去把它指向到A类自己的对象实例上,属于多此一举,因此第二个参数加不加都行,不加写null就行) |
综上大家应该理解其用法了吧,有时第二个参数为null,有时第三个参数可以不要,这些都跟你匿名函数里 代码中访问的方式紧密相关
总结:
1、一般匿名函数中有$this->name类似这样用 $this访问属性方式时,你在使用bind绑定时 ,第二个参数肯定要写,写出你绑定那个对象实例,第三个参数要不要呢,要看你访问的这个属性,在绑定对象中的权限属性,如果是private,protected 你要使用第三个参数 使其变为公有属性, 如果本来就是公有,你可以省略,也可以不省略
2、一般匿名函数中是 类名::静态属性 类似这样的访问方式(比如例子中A::$weight),你在使用bind绑定时,第二个参数可以写null,也可以写出具体的对象实例,一般写null就行(写了具体对象实例多此一举),第三个参数写不写还是得看你访问的这个静态属性的权限是 private 还是 public,如果是私有private或受保护protected的,你就得第三个参数必须写,才能使其权限变为公有属性 正常访问,如果本来就是公有public可以不用写,可以省略。
原文链接:https://blog.csdn.net/qq_27718961/article/details/91043221