PHP Closure(闭包)类详解
<h1> Closure</h1><p>面向对象变成语言代码的复用主要采用继承来实现,而函数的复用,就是通过闭包来实现。这就是闭包的设计初衷。</p>
<p> </p>
<p><strong>注:</strong>PHP里面闭包函数是为了复用函数而设计的语言特性,如果在闭包函数里面访问指定域的变量,使用use关键字来实现。</p>
<p> </p>
<p><span style="color: rgba(255, 0, 0, 1)"><strong>PHP具有面向函数的编程特性,但是也是面向对象编程语言,PHP 会自动把闭包函数转换成内置类 Closure 的对象实例</strong></span>,依赖Closure 的对象实例又给闭包函数添加了更多的能力。 </p>
<p> </p>
<p>闭包不能被实例(私有构造函数),也不能被继承(finally 类)。可以通过反射来判断闭包实例是否能被实例,继承。</p>
<p> </p>
<h2>匿名函数</h2>
<p>匿名函数,是php5.3的时候引入的,又称为Anonymous functions。字面意思也就是没有定义名字的函数。</p>
<p><br>提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它。声明一个匿名函数是这样:</p>
<p> </p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$say = function() {
return '我是匿名函数';
}; //带结束符<br><br>echo $say(); //这是最直接调用匿名函数方式<br><br>function test(Closure $callback){ <br> return $callback(); <br>} <br><br>echo test($say); //这是间接调用匿名函数方式<br><br></pre>
<p> 当然也可以这样写 : <br> echo test( function() {<br> return '我是匿名函数';<br> });</p>
</div>
<p> </p>
<p>可以看到,匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数,调用方法也相同:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">$func = function( $param ) {
echo $param;
};
$func( 'some string' );
//输出:
//some string
</pre>
</div>
<p> </p>
<p>顺便提一下,PHP在引入闭包之前,也有一个可以创建匿名函数的函数:create function,但是代码逻辑只能写成字符串,这样看起来很晦涩并且不好维护,所以很少有人用。</p>
<p> </p>
<h2>实现闭包</h2>
<p><br>将匿名函数在普通函数中当做参数传入,也可以在函数中被返回。这就实现了一个简单的闭包。</p>
<p> </p>
<p><strong>连接闭包和外界变量的关键字:<span style="color: rgba(255, 0, 0, 1)">USE</span></strong></p>
<p><br>PHP在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用use关键字。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">function getMoney() {
$rmb = 1;
$func = function() use ( $rmb ) {
echo $rmb;
//把$rmb的值加1
$rmb++;
};
$func();
echo $rmb; //闭包内的变量改变了,但是闭包外没有改变。
}
getMoney();
//输出:
//1
//1
</pre>
</div>
<p> </p>
<p>在上面我们看见匿名函数不能改变上下文的变量,是因为use所引用的也只不过是变量的一个副本clone而已(非完全引用变量本身)。</p>
<p>如果我们想在匿名函数中改变上下文的变量呢?想要完全引用变量,而不是复制呢?要达到这种效果,在变量前加一个 & 符号即可。</p>
<p> </p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">function getMoney() {
$rmb = 1;
$func = function() use ( &$rmb ) {
echo $rmb;
//把$rmb的值加1
$rmb++;
};
$func();
echo $rmb;
}
getMoney();
//输出:
//1
//2
</pre>
</div>
<p> </p>
<p>好,这样匿名函数就可以引用上下文的变量了。<span style="color: rgba(255, 0, 0, 1)">如果将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量</span>,这样形成‘闭包’这个概念可能会更清晰一些。</p>
<p>根据描述我们再改变一下上面的例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">function getMoneyFunc() {
$rmb </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
$func </span>= function() use ( &<span style="color: rgba(0, 0, 0, 1)">$rmb ) {
echo $rmb.</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"><br></span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">把$rmb的值加1</span>
$rmb++<span style="color: rgba(0, 0, 0, 1)">;
};
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> $func;
}
$getMoney </span>=<span style="color: rgba(0, 0, 0, 1)"> getMoneyFunc(); //匿名函数返回给外界,保存了$rmb的变量
$getMoney();
$getMoney();
$getMoney();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">1
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">2
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">3<br><br>从例子中理解“将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量”,将匿名函数返回给外界后,$rmb这个引用变量就被保存了,提升成了全局变量(全局变量,类似静态变量),而后面每次再调用匿名函数,传入的都是这个保存后的值</span></pre>
</div>
<p> </p>
<p> </p>
<h3>总结:</h3>
<p><strong><span style="color: rgba(255, 0, 0, 1)">闭包函数不能直接访问闭包外的变量,而是通过use 关键字来调用上下文变量(闭包外的变量),也就是说通过use来引用上下文的变量;</span></strong></p>
<p><strong><span style="color: rgba(255, 0, 0, 1)">闭包内所引用的变量不能被外部所访问(即,内部对变量的修改,外部不受影响),若想要在闭包内对变量的改变从而影响到上下文变量的值,需要使用&的引用传参。</span></strong></p>
<blockquote>
<p> <strong><strong>use所引用的是变量的复制(副本而),并不是完全引用变量。如果要达到引用的效果,就需要使用 & 符号,进行引用传递参数;</strong></strong></p>
</blockquote>
<p> </p>
<p>如果我们要调用一个类里面的匿名函数呢?直接上demo</p>
<div class="cnblogs_code">
<pre><?<span style="color: rgba(0, 0, 0, 1)">php
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> A {
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> function testA() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> function($i) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回匿名函数</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> $i+<span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">;
};
}
}
function B(Closure $callback)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> $callback(<span style="color: rgba(128, 0, 128, 1)">200</span><span style="color: rgba(0, 0, 0, 1)">);
}
$a </span>=<span style="color: rgba(0, 0, 0, 1)"> B(A::testA());
print_r($a);</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 300</span></pre>
</div>
<p>其中的A::testA()返回的就是一个无名funciton。</p>
<p> </p>
<h2 id="绑定的概念">绑定的概念</h2>
<p>上面的例子的Closure只是全局的的匿名函数,好了,那我们现在想指定一个类有一个匿名函数。也可以理解说,这个匿名函数的访问范围不再是全局的了,而是一个类的访问范围。</p>
<p>那么我们就需要将“一个匿名函数绑定到一个类中”。</p>
<p> </p>
<p><span style="color: rgba(0, 0, 0, 1)">PHP Closure 类是用于代表匿名函数的类,匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象,Closure类摘要如下:</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">Closure {
__construct ( void )
public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])
public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])
}
</pre>
</div>
<p> </p>
<p>参数说明:</p>
<p><strong>closure</strong><br>需要绑定的匿名函数。</p>
<p><strong>newthis</strong><br>需要绑定到匿名函数的对象,或者 NULL 创建未绑定的闭包。</p>
<p><strong>newscope</strong><br>想要绑定给闭包的类作用域,或者 'static' 表示不改变。如果传入一个对象,则使用这个对象的类型名。 类作用域用来决定在闭包中 $this 对象的 私有、保护方法 的可见性。(备注:可以传入类名或类的实例,默认值是 'static', 表示不改变。) </p>
<p><strong>返回值</strong>:<br>返回一个新的 Closure 对象 或者在失败时返回 FALSE</p>
<p> </p>
<p>方法说明:</p>
<blockquote>
<p> </p>
<pre class="brush:php;gutter:true;">Closure::__construct — 用于禁止实例化的构造函数
Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。
Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。</pre>
</blockquote>
<p>除了此处列出的方法,还有一个 __invoke 方法。这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用闭包对象的过程与它无关。</p>
<p> </p>
<p><span style="color: rgba(0, 0, 0, 1)"><strong>Closure::bind是Closure::bindTo的静态版本</strong></span></p>
<p> </p>
<blockquote>
<pre><br>深入理解bind参数:<br><br>要理解bing参数,先要理解类属性的可访问权限 public,private(子类不可访问),protect(子类可访问,类外不可以访问)。<br>php的public、protected、private三种访问控制模式的区别<br><br>public: 公有类型 在子类中可以通过self::var调用public方法或属性,parent::method调用父类方法<br> 在实例中可以能过$obj->var 来调用 public类型的方法或属性<br>protected: 受保护类型在子类中可以通过self::var调用protected方法或属性,parent::method调用父类方法<br> 在实例中不能通过$obj->var 来调用protected类型的方法或属性<br>private: 私有类型该类型的属性或方法只能在该类中使用,在该类的实例、子类中、子类的实例中都不能调用私有类型的属性和方法<br><br>不能通过 $this 访问静态变量,静态方法里面也不可用 $this (原因:静态属性属于类本身而不属于类的任何实例。静态属性可以被看做是存储在类当中的全局变量,可以在任何地方通过类来访问它们)<br>在类外不能通过 类名::私有静态变量,只能在类里面通过self,或者static 访问私有静态变量:<br><br>第一个参数:需要绑定的匿名函数。<br>第二个参数:关于bind的第二个参数为object还是null,取决于第一个参数闭包中是否用到了`$this`的上下文环境。(绑定的对象决定了函数中的$this的取值)<br>若闭包中用到了`$this`,则第2个参数不可为null,只能为object实例对象;若闭包中用到了静态访问(::操作符),第2个参数就可以为null,也可以为object<br>第三个参数(作用域):是控制闭包的作用域的,如果闭包中访问的是 private 属性,就需要第3个参数提升闭包的访问权限,若闭包中访问的是public属性,第三个参数可以不用。只有需要改变访问权限时才要,传对象,类名都可以。</pre>
<p> <br><br></p>
</blockquote>
<p><span style="color: rgba(255, 0, 0, 1)"><strong> 第2个参数是给闭包绑定`$this`对象的,第3个参数是给闭包绑定作用域的。</strong></span></p>
<p><strong>通过成员的可访问行来举例子理解:</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> A{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(128, 0, 128, 1)">$name</span> = '王力宏'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$age</span> = '30'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$weight</span> = '70kg'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(128, 0, 128, 1)">$address</span> = '中国'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(128, 0, 128, 1)">$height</span> = '180cm'<span style="color: rgba(0, 0, 0, 1)">;
}<br><br></span></pre>
</div>
<p> </p>
<p><strong> </strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$obj</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">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</span>
<span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$obj</span>->address;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 中国</span>
<span style="color: rgba(0, 0, 255, 1)">echo</span> A::<span style="color: rgba(128, 0, 128, 1)">$height</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 180cm</span>
<span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(128, 0, 128, 1)">$obj</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$obj</span>->address;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">实例对象可以获得公有属性,$obj->name等私有属性肯定不行 上面例子已列出报错</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$fun</span>();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 中国</span>
<span style="color: rgba(128, 0, 128, 1)">$fun2</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> A::<span style="color: rgba(128, 0, 128, 1)">$height</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">类可以直接访问公有静态属性,但A::$weight肯定不行,因为weight为私有属性</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$fun2</span>();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 180cm</span></pre>
</div>
<p> </p>
<p>以上都理解的情况下 我们来看看这样的情况,有如下匿名函数:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 0, 0, 1)">name;
}
或者
</span><span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> A::<span style="color: rgba(128, 0, 128, 1)">$height</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$fun</span>();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会报错的</span></pre>
</div>
<p>其实单独这段代码是肯定不能运行 调用的,因为里面有个$this,程序压根不知道你这个$this是代表那个对象 或 那个类(并且就算知道那个对象或类,该对象是否拥有name属性,如果没有照样会有问题)</p>
<p>因此想让其正常运行肯定有前提条件啊(就好比你想遍历某个数组一样,如果这个数组压根你就没提前定义 声明 肯定会报错的)<br>如果这样:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">name是私有属性,需要第3个参数</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 0, 0, 1)">name;
} ;
</span><span style="color: rgba(128, 0, 128, 1)">$name</span> = Closure::bind(<span style="color: rgba(128, 0, 128, 1)">$fun</span>,<span style="color: rgba(0, 0, 255, 1)">new</span> A() ,'A'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$name</span>();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 王力宏 </span>
该函数返回一个全新的 Closure匿名的函数,和原来匿名函数<span style="color: rgba(128, 0, 128, 1)">$fun一模一样</span>,只是其中<span style="color: rgba(128, 0, 128, 1)">$this被指向了A实例对象</span>,这样就能访问address属性了。</pre>
</div>
<p>使用了bind函数后 ,</p>
<p>其实是匿名函数里的$this被指定到了或绑定到了A实例对象上了</p>
<div class="cnblogs_code">
<pre>Closure::bind(<span style="color: rgba(128, 0, 128, 1)">$fun</span>,<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A() );
这个使用 你可以理解成对匿名函数做了如下过程</span>:
<span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(128, 0, 128, 1)">$this</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 0, 0, 1)">name;
} ;</span></pre>
</div>
<p>你可以想象认为,$this就成了A类的实例对象了,然后在去访问name属性,就和我们正常实例化类访问成员属性一样,上面2中的例子$obj = new A()就是这样,(因为$this是关键字,在这里我们其实不能直接$this = new A();这么写,为了好理解我写成$this,但是原理还是这个意思),但是我们都知道因为name属性是私有的,上面2中我已说过,实例对象不能访问私有属性,那该怎么办呢,于是添加第三个参数就很重要了,一般传入传入一个对应对象,或对应类名(对应的意思是:匿名函数中$this-name想获取name属性值,你这个$this想和那个类和对象绑定在一起呢,就是第二个参数,这时你第三个参数写和第二个参数写一样的对象或类就行了,就是作用域为这个对象或类,这就会让原理的name私有属性变为公有属性)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">address为公开,第3个参数可以不需要</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 0, 0, 1)">name;
} ;
</span><span style="color: rgba(128, 0, 128, 1)">$address</span> = Closure::bind(<span style="color: rgba(128, 0, 128, 1)">$fun</span>,<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A());
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$address</span>();</pre>
</div>
<p>这里数一下bind这次为什么没添加第三个参数,因为我们要访问的address属性是公有的,一个对象实例是可以直接访问公有属性的,这个例子中只要匿名函数中$this被指向了A对象实例(或者叫绑定也可以),就能访问到公有属性,所以可以不用添加第三个参数,当然你加上了第三个参数 如这样Closure::bind($fun2, new A(), ‘A’ );或Closure::bind($fun2, new A(), nwe A() );不影响 照样运行,就好比把原来公有属性 变为公有属性 不影响的(一般当我们访问的属性为私有属性时,才使用第三个参数改变作用域 ,使其变为公有属性)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 128, 1)">$fun</span> = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">访问私有静态属性,访问静态属性,第2个参数可以为null,访问私有属性,需要第3个参数提高作用域</span></pre>
<pre> //return A::$weight;这样写,会报错,提示不能通过类名访问私有静态变量<br> //weight 为类的私有属性,只可在类中访问<br> return self::$weight;</pre>
<pre><span style="color: rgba(0, 0, 0, 1)"> }; </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">echo $fun(); 运行会报错 因为weight为私有属性 。</span> <span style="color: rgba(128, 0, 128, 1)">$weight</span> = Closure::bind(<span style="color: rgba(128, 0, 128, 1)">$fun</span>,<span style="color: rgba(0, 0, 255, 1)">null</span>,'A'); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过bind函数作用,返回一个和$fun匿名函数一模一样的匿名函数,只是该匿名函数中A::$weight, weight属性由私有变成公有属性了。</span> <span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$weight</span>();</pre>
</div>
<p><br>为什么第二个参数又成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就行)</p>
<p> </p>
<blockquote>
<p>综上大家应该理解其用法了吧,有时第二个参数为null,有时第三个参数可以不要,这些都跟你匿名函数里 代码中访问的方式紧密相关<br>总结:<br>1、一般匿名函数中有$this->name类似这样用 $this访问属性方式时,你在使用bind绑定时 ,第二个参数肯定要写,写出你绑定那个对象实例,第三个参数要不要呢,要看你访问的这个属性,在绑定对象中的权限属性,如果是private,protected 你要使用第三个参数 使其变为公有属性, 如果本来就是公有,你可以省略,也可以不省略<br>2、一般匿名函数中是类名::静态属性类似这样的访问方式(比如例子中A::$weight),你在使用bind绑定时,第二个参数可以写null,也可以写出具体的对象实例,一般写null就行(写了具体对象实例多此一举),第三个参数写不写还是得看你访问的这个静态属性的权限是 private 还是 public,如果是私有private或受保护protected的,你就得第三个参数必须写,才能使其权限变为公有属性 正常访问,如果本来就是公有public可以不用写,可以省略</p>
<p>3、需要提升属性作用域时,第3个参数需要传,传对象或者类名都可以。</p>
</blockquote>
<p> </p>
<p><span style="color: rgba(0, 0, 0, 1)">例子:</span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:php;gutter:true;">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;
</pre>
</div>
<p> </p>
<p>通过上面的例子,可以看出<strong>函数复用得,可以把函数挂在不同的类上,或者对象上</strong>。</p>
<p> </p>
<p><span style="font-size: 14pt"><strong>闭包函数的应用:</strong></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 一个基本的购物车,包括一些已经添加的商品和每种商品的数量
*
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Cart {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义商品价格</span>
<span style="color: rgba(0, 0, 255, 1)">const</span> PRICE_BUTTER = <span style="color: rgba(128, 0, 128, 1)">10.00</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> PRICE_MILK = <span style="color: rgba(128, 0, 128, 1)">30.33</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> PRICE_EGGS = <span style="color: rgba(128, 0, 128, 1)">80.88</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> $products =<span style="color: rgba(0, 0, 0, 1)"> array();
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 添加商品和数量
*
* @access public
* @param string 商品名称
* @param string 商品数量
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> function add($item, $quantity) {
$</span><span style="color: rgba(0, 0, 255, 1)">this</span>->products[$item] =<span style="color: rgba(0, 0, 0, 1)"> $quantity;
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 获取单项商品数量
*
* @access public
* @param string 商品名称
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> function getQuantity($item) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> isset($<span style="color: rgba(0, 0, 255, 1)">this</span>->products[$item]) ? $<span style="color: rgba(0, 0, 255, 1)">this</span>-><span style="color: rgba(0, 0, 0, 1)">products[$item] : FALSE;
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 获取总价
*
* @access public
* @param string 税率
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> function getTotal($tax) {
$total </span>= <span style="color: rgba(128, 0, 128, 1)">0.00</span><span style="color: rgba(0, 0, 0, 1)">;
$callback </span>= function ($quantity, $item) use ($tax, &<span style="color: rgba(0, 0, 0, 1)">$total) {
$pricePerItem </span>= constant(__CLASS__ . <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">::PRICE_</span><span style="color: rgba(128, 0, 0, 1)">"</span> . strtoupper($item)); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用以上对应的常量</span>
$total += ($pricePerItem * $quantity) * ($tax + <span style="color: rgba(128, 0, 128, 1)">1.0</span><span style="color: rgba(0, 0, 0, 1)">);
};
array_walk($</span><span style="color: rgba(0, 0, 255, 1)">this</span>-><span style="color: rgba(0, 0, 0, 1)">products, $callback);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> round($total, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
$my_cart </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Cart;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 往购物车里添加商品及对应数量</span>
$my_cart->add(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">butter</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">);
$my_cart</span>->add(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">milk</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">);
$my_cart</span>->add(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">eggs</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 128, 1)">12</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打出出总价格,其中有 3% 的销售税.</span>
echo $my_cart->getTotal(<span style="color: rgba(128, 0, 128, 1)">0.03</span>);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 1196.4</span></pre>
</div>
<p> </p>
<p><strong> 给类的私有属性赋值:</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> A {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(128, 0, 128, 1)">$attr</span> = <span style="color: rgba(0, 0, 255, 1)">array</span><span style="color: rgba(0, 0, 0, 1)">();
}
</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> B {
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">function</span> bind (A <span style="color: rgba(128, 0, 128, 1)">$a</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> \Closure::bind(<span style="color: rgba(0, 0, 255, 1)">function</span> () <span style="color: rgba(0, 0, 255, 1)">use</span> (<span style="color: rgba(128, 0, 128, 1)">$a</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">给私有属性赋值</span>
<span style="color: rgba(128, 0, 128, 1)">$a</span>->attr =<span style="color: rgba(0, 0, 0, 1)"> [
</span>'color' => 'red',
'weight' => '1.0',<span style="color: rgba(0, 0, 0, 1)">
];
}</span>, <span style="color: rgba(0, 0, 255, 1)">null</span>, A::<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">我们在对象外部给私有对象赋值时,需要通过\Closure::bind ,来提高闭包作用域进行赋值</span>
<span style="color: rgba(128, 0, 128, 1)">$a</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不能对私有属性直接进行$a->attr = ['color' => 'red','weight' => '1.0'];</span>
<span style="color: rgba(0, 128, 128, 1)">call_user_func</span>(B::bind(<span style="color: rgba(128, 0, 128, 1)">$a</span><span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">print_r</span>(<span style="color: rgba(128, 0, 128, 1)">$a</span>);</pre>
</div>
<p> </p>
<p> 通过上面的几个例子,其实匿名绑定的理解就不难了....我们在看一个扩展的demo(引入trait特性)</p>
<p><strong>给类动态的添加方法</strong></p>
<p>官方文档有例子,点击查看</p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 给类动态添加新方法
*
* @author fantasy
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
trait DynamicTrait {
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 自动调用类中存在的方法
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> __call(<span style="color: rgba(128, 0, 128, 1)">$name</span>, <span style="color: rgba(128, 0, 128, 1)">$args</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(<span style="color: rgba(0, 128, 128, 1)">is_callable</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(128, 0, 128, 1)">$name</span><span style="color: rgba(0, 0, 0, 1)">)){
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 128, 128, 1)">call_user_func</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(128, 0, 128, 1)">$name</span>, <span style="color: rgba(128, 0, 128, 1)">$args</span><span style="color: rgba(0, 0, 0, 1)">);
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> \RuntimeException("Method {<span style="color: rgba(128, 0, 128, 1)">$name</span>} does not exist"<span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 添加方法
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> __set(<span style="color: rgba(128, 0, 128, 1)">$name</span>, <span style="color: rgba(128, 0, 128, 1)">$value</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(128, 0, 128, 1)">$name</span> = <span style="color: rgba(0, 128, 128, 1)">is_callable</span>(<span style="color: rgba(128, 0, 128, 1)">$value</span>)?
<span style="color: rgba(128, 0, 128, 1)">$value</span>->bindTo(<span style="color: rgba(128, 0, 128, 1)">$this</span>, <span style="color: rgba(128, 0, 128, 1)">$this</span>):
<span style="color: rgba(128, 0, 128, 1)">$value</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* 只带属性不带方法动物类
*
* @author fantasy
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Animal {
</span><span style="color: rgba(0, 0, 255, 1)">use</span><span style="color: rgba(0, 0, 0, 1)"> DynamicTrait;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(128, 0, 128, 1)">$dog</span> = '汪汪队'<span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(128, 0, 128, 1)">$animal</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Animal;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 往动物类实例中添加一个方法获取实例的私有属性$dog</span>
<span style="color: rgba(128, 0, 128, 1)">$animal</span>->getdog = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$this</span>-><span style="color: rgba(0, 0, 0, 1)">dog;
};
</span><span style="color: rgba(0, 0, 255, 1)">echo</span> <span style="color: rgba(128, 0, 128, 1)">$animal</span>->getdog();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 汪汪队</span></pre>
</div>
<p> </p>
<p>获取类属性:第3个参数 score 范围 设置为null 时,只能获取到 public 属性。</p>
<p><span style="color: rgba(255, 0, 0, 1)">如果希望完全取消绑定,则需要将闭包和范围都设置为null</span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MyClass
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(128, 0, 128, 1)">$foo</span> = 'a'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(128, 0, 128, 1)">$bar</span> = 'b'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(128, 0, 128, 1)">$baz</span> = 'c'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">*
* @param bool $all 是否获取全部的属性
*
* @return array
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">function</span> toArray(<span style="color: rgba(128, 0, 128, 1)">$all</span> = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Only public variables</span>
<span style="color: rgba(128, 0, 128, 1)">$callback</span> = (<span style="color: rgba(0, 0, 255, 1)">function</span> (<span style="color: rgba(128, 0, 128, 1)">$obj</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> get_object_vars — 返回由对象属性组成的关联数组 ,在类内调用时,会返回所有的属性(private,protect,public), 在类外使用只返回public</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 128, 128, 1)">get_object_vars</span>(<span style="color: rgba(128, 0, 128, 1)">$obj</span><span style="color: rgba(0, 0, 0, 1)">);
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">指定作用域 为 null 则会返回 public 属性; 指定作用域默认 ‘static’,则会返回所有属性</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(128, 0, 128, 1)">$all</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(128, 0, 128, 1)">$callback</span> = <span style="color: rgba(128, 0, 128, 1)">$callback</span>->bindTo(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">类内获取public属性</span>
<span style="color: rgba(128, 0, 128, 1)">$callback</span> = <span style="color: rgba(128, 0, 128, 1)">$callback</span>->bindTo(<span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">$callback</span>(<span style="color: rgba(128, 0, 128, 1)">$this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(128, 0, 128, 1)">$obj</span> = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MyClass;
</span><span style="color: rgba(128, 0, 128, 1)">$vars</span> = <span style="color: rgba(0, 128, 128, 1)">get_object_vars</span>(<span style="color: rgba(128, 0, 128, 1)">$obj</span>); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> get_object_vars 类外使用,获取到的是 public 属性</span>
<span style="color: rgba(128, 0, 128, 1)">$vars</span> = <span style="color: rgba(128, 0, 128, 1)">$obj</span>->toArray();</pre>
</div>
<p> </p>
<p> </p>
<p>总结:</p>
<p><span style="color: rgba(255, 0, 0, 1)"><strong>1. 闭包内如果用 $this, 则 $this 只能调用非静态的属性,这和实际类中调用原则是一致的,且 Closure::bind() 方法的第2个参数不能为null,必须是一个实例 (因为$this,必须在实例中使用),第三个参数可以是实例,可以是类字符串,或 static;</strong></span></p>
<p><span style="color: rgba(255, 0, 0, 1)"><strong>2. 闭包内调用静态属性时,闭包必须声明为 static,同时Closure::bind()方法的第2个参数需要为null,因为 静态属性不需要实例,第3个参数可以是类字符串,实例,static.</strong></span></p>
<p> </p><br><br>
来源:https://www.cnblogs.com/echojson/p/10957362.html
頁:
[1]