思念的花 發表於 2020-7-11 00:28:00

PHP Closure(闭包)类详解

<h1><span style="font-size: 16px; font-family: &quot;Microsoft YaHei&quot;">闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。 在php中,闭包函数一般就是匿名函数.</span>&nbsp;</h1>
<h1>Closure</h1>
<p>面向对象变成语言代码的复用主要采用继承来实现,而函数的复用,就是通过闭包来实现。这就是闭包的设计初衷。</p>
<p><strong>注:</strong>PHP里面闭包函数是为了复用函数而设计的语言特性,如果在闭包函数里面访问指定域的变量,使用use关键字来实现。</p>
<p><strong>PHP具有面向函数的编程特性,但是也是面向对象编程语言,PHP 会自动把闭包函数转换成内置类 Closure 的对象实例</strong>,依赖Closure 的对象实例又给闭包函数添加了更多的能力。&nbsp;</p>
<p>闭包不能被实例(私有构造函数),也不能被继承(finally 类)。可以通过反射来判断闭包实例是否能被实例,继承。</p>
<h2>匿名函数</h2>
<p>匿名函数,是php5.3的时候引入的,又称为Anonymous functions。字面意思也就是没有定义名字的函数。</p>
<p>提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它。声明一个匿名函数是这样:</p>
<div class="cnblogs_code">
<pre>$say =<span style="color: rgba(0, 0, 0, 1)"> function() {
  </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">我是匿名函数</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)">带结束符<br><span style="color: rgba(0, 0, 0, 1)">echo $say(); </span><br><br></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这是最直接调用匿名函数方式<br><span style="color: rgba(0, 0, 0, 1)">function test(Closure $callback)</span><br><span style="color: rgba(0, 0, 0, 1)">{ return $callback();} </span><br><span style="color: rgba(0, 0, 0, 1)">echo test($say);</span> <br><br></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这是间接调用匿名函数方式<br></span></pre>
<p>当然也可以这样写 :<br>echo test( function() {<br>  return '我是匿名函数';<br>});</p>
<p>//例一</p>
<p>//在函数里定义一个匿名函数,并且调用它</p>
<p>function printStr() {</p>
<p>&nbsp; &nbsp; $func = function( $str ) {</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; echo $str;</p>
<p>&nbsp; &nbsp; };</p>
<p>&nbsp; &nbsp; $func( 'some string' );</p>
<p>}</p>
<p>printStr();&nbsp;&nbsp;</p>
<p>//例二</p>
<p>//在函数中把匿名函数返回,并且调用它</p>
<p>function getPrintStrFunc() {</p>
<p>&nbsp; &nbsp; $func = function( $str ) {</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; echo $str;</p>
<p>&nbsp; &nbsp; };</p>
<p>&nbsp; &nbsp; return $func;</p>
<p>}</p>
<p>$printStrFunc = getPrintStrFunc();</p>
<p>$printStrFunc( 'some string' );</p>
<p>//例三</p>
<p>//把匿名函数当做参数传递,并且调用它</p>
<p>function callFunc( $func ) {</p>
<p>&nbsp; &nbsp; $func( 'some string' );</p>
<p>}</p>
<p>$printStrFunc = function( $str ) {</p>
<p>&nbsp; &nbsp; echo $str;</p>
<p>};</p>
<p>callFunc( $printStrFunc );</p>
<p>//也可以直接将匿名函数进行传递。如果你了解js,这种写法可能会很熟悉</p>
<p>callFunc( function( $str ) {</p>
<p>&nbsp; &nbsp; echo $str;</p>
<p>} );</p>










</div>
<p>可以看到,匿名函数因为没有名字,如果要使用它,需要将其返回给一个变量。匿名函数也像普通函数一样可以声明参数,调用方法也相同:</p>
<div class="cnblogs_code">
<pre>$func =<span style="color: rgba(0, 0, 0, 1)"> function( $param ) {
    echo $param;
};
$func( </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">some string</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)">输出:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">some string</span></pre>
</div>
<p>顺便提一下,PHP在引入闭包之前,也有一个可以创建匿名函数的函数:create function,但是代码逻辑只能写成字符串,这样看起来很晦涩并且不好维护,所以很少有人用。</p>
<h2>实现闭包</h2>
<p>将匿名函数在普通函数中当做参数传入,也可以在函数中被返回。这就实现了一个简单的闭包。</p>
<p><strong>连接闭包和外界变量的关键字:USE</strong></p>
<p>PHP在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用use关键字。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">function getMoney() {
    $rmb </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
    $func </span>=<span style="color: rgba(0, 0, 0, 1)"> function() use ( $rmb ) {
      echo $rmb;
      </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)">;
    };
    $func();
    echo $rmb; </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, 0, 1)">}
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)">1</span></pre>
</div>
<p>在上面我们看见匿名函数不能改变上下文的变量,是因为use所引用的也只不过是变量的一个副本clone而已(非完全引用变量本身)。</p>
<p>如果我们想在匿名函数中改变上下文的变量呢?想要完全引用变量,而不是复制呢?要达到这种效果,在变量前加一个 &amp; 符号即可。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">function getMoney() {
    $rmb </span>= <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
    $func </span>= function() use ( &amp;<span style="color: rgba(0, 0, 0, 1)">$rmb ) {
      echo $rmb;
      </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)">;
    };
    $func();
    echo $rmb;
}
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></pre>
</div>
<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 ( &amp;<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)">&lt;br&gt;</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>= getMoneyFunc(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">匿名函数返回给外界,保存了$rmb的变量</span>
<span style="color: rgba(0, 0, 0, 1)">$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从例子中理解“将匿名函数返回给外界,匿名函数会保存use所引用的变量,而外界则不能得到这些变量”,将匿名函数返回给外界后,$rmb这个引用变量就被保存了,提升成了全局变量(全局变量,类似静态变量),而后面每次再调用匿名函数,传入的都是这个保存后的值</span></pre>
</div>
<h3>总结:</h3>
<p><strong>闭包函数不能直接访问闭包外的变量,而是通过use 关键字来调用上下文变量(闭包外的变量),也就是说通过use来引用上下文的变量;</strong></p>
<p><strong>闭包内所引用的变量不能被外部所访问(即,内部对变量的修改,外部不受影响),若想要在闭包内对变量的改变从而影响到上下文变量的值,需要使用&amp;的引用传参。</strong></p>
<p><strong>&nbsp;<strong><strong>use所引用的是变量的复制(副本而),并不是完全引用变量。如果要达到引用的效果,就需要使用 &amp; 符号,进行引用传递参数;</strong></strong></strong></p>
<p>&nbsp;</p>
<p>如果我们要调用一个类里面的匿名函数呢?直接上demo</p>
<div class="cnblogs_code">
<pre>&lt;?<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>
<h2 id="绑定的概念">绑定的概念</h2>
<p>上面的例子的Closure只是全局的的匿名函数,好了,那我们现在想指定一个类有一个匿名函数。也可以理解说,这个匿名函数的访问范围不再是全局的了,而是一个类的访问范围。</p>
<p>那么我们就需要将“一个匿名函数绑定到一个类中”。</p>
<p>&nbsp;</p>
<p>PHP&nbsp;Closure 类是用于代表匿名函数的类,匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象,Closure类摘要如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Closure {
    __construct ( </span><span style="color: rgba(0, 0, 255, 1)">void</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> Closure bind (Closure $closure , <span style="color: rgba(0, 0, 255, 1)">object</span> $newthis [, mixed $newscope = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">static</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> ])
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> Closure bindTo (<span style="color: rgba(0, 0, 255, 1)">object</span> $newthis [, mixed $newscope = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">static</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> ])
}</span></pre>
</div>
<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>&nbsp;</p>
<p>方法说明:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Closure::__construct — 用于禁止实例化的构造函数
Closure::bind — 复制一个闭包,绑定指定的$this对象和类作用域。
Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域。</span></pre>
</div>
<p>除了此处列出的方法,还有一个 __invoke 方法。这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用闭包对象的过程与它无关。</p>
<p>&nbsp;</p>
<p><strong>Closure::bind是Closure::bindTo的静态版本</strong></p>
<p>深入理解bind参数:</p>
<p>要理解bing参数,先要理解类属性的可访问权限 public,private(子类不可访问),protect(子类可访问,类外不可以访问)。<br>php的public、protected、private三种访问控制模式的区别</p>
<p>public: 公有类型 在子类中可以通过self::var调用public方法或属性,parent::method调用父类方法<br>    在实例中可以能过$obj-&gt;var 来调用 public类型的方法或属性<br>protected: 受保护类型在子类中可以通过self::var调用protected方法或属性,parent::method调用父类方法<br>    在实例中不能通过$obj-&gt;var 来调用protected类型的方法或属性<br>private: 私有类型该类型的属性或方法只能在该类中使用,在该类的实例、子类中、子类的实例中都不能调用私有类型的属性和方法</p>
<p>不能通过 $this 访问静态变量,静态方法里面也不可用 $this (原因:静态属性属于类本身而不属于类的任何实例。静态属性可以被看做是存储在类当中的全局变量,可以在任何地方通过类来访问它们)<br>在类外不能通过 类名::私有静态变量,只能在类里面通过self,或者static 访问私有静态变量:</p>
<p>第一个参数:需要绑定的匿名函数。<br>第二个参数:关于bind的第二个参数为object还是null,取决于第一个参数闭包中是否用到了`$this`的上下文环境。(绑定的对象决定了函数中的$this的取值)<br>若闭包中用到了`$this`,则第2个参数不可为null,只能为object实例对象;若闭包中用到了静态访问(::操作符),第2个参数就可以为null,也可以为object<br>第三个参数(作用域):是控制闭包的作用域的,如果闭包中访问的是 private 属性,就需要第3个参数提升闭包的访问权限,若闭包中访问的是public属性,第三个参数可以不用。只有需要改变访问权限时才要,传对象,类名都可以。<br>  </p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong>第2个参数是给闭包绑定`$this`对象的,第3个参数是给闭包绑定作用域的。</strong></p>
<p><strong><strong>通过成员的可访问行来举例子理解:</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> $name = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">王力宏</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">protected</span> $age = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">30</span><span style="color: rgba(128, 0, 0, 1)">'</span><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> $weight = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">70kg</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
      </span><span style="color: rgba(0, 0, 255, 1)">public</span> $address = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">中国</span><span style="color: rgba(128, 0, 0, 1)">'</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> $height = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">180cm</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<div class="cnblogs_code">
<pre>$obj = <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-&gt;name;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">报错 Cannot access private property A::$name
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">echo $obj-&gt;age;</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">报错 Cannot access protected property A::$age
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">echo A::$weight; </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">报错 Cannot access private property A::$weight</span>
echo $obj-&gt;address;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 中国</span>
echo A::$height;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 180cm</span>
$fun =<span style="color: rgba(0, 0, 0, 1)"> function(){
      $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> $obj-&gt;address;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">实例对象可以获得公有属性,$obj-&gt;name等私有属性肯定不行 上面例子已列出报错</span>
<span style="color: rgba(0, 0, 0, 1)">}
echo $fun();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">正常 输出 中国</span>
$fun2 =<span style="color: rgba(0, 0, 0, 1)"> function(){
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> A::$height;<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)">}
echo $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>
<div class="cnblogs_code">
<pre>$fun =<span style="color: rgba(0, 0, 0, 1)"> function(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> $<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">name;
}
或者
$fun </span>=<span style="color: rgba(0, 0, 0, 1)"> function(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> A::$height;
}
echo $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>$fun =<span style="color: rgba(0, 0, 0, 1)"> function(){
    </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(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">name;
} ;
$name </span>= Closure::bind($fun,<span style="color: rgba(0, 0, 255, 1)">new</span> A() ,<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);                                                                                                                                                         
echo $name();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 王力宏 </span>
该函数返回一个全新的 Closure匿名的函数,和原来匿名函数$fun一模一样,只是其中$this被指向了A实例对象,这样就能访问address属性了。</pre>
</div>
<p>使用了bind函数后 ,</p>
<p>其实是匿名函数里的$this被指定到了或绑定到了A实例对象上了</p>
<div class="cnblogs_code">
<pre>Closure::bind($fun,<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A() );
这个使用 你可以理解成对匿名函数做了如下过程:
$fun </span>=<span style="color: rgba(0, 0, 0, 1)"> function(){
   $</span><span style="color: rgba(0, 0, 255, 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(0, 0, 255, 1)">this</span>-&gt;<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>$fun =<span style="color: rgba(0, 0, 0, 1)"> function(){
    </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(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">name;
} ;
$address </span>= Closure::bind($fun,<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> A());
echo $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>$fun =<span style="color: rgba(0, 0, 0, 1)"> function() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">访问私有静态属性,访问静态属性,第2个参数可以为null,访问私有属性,需要第3个参数提高作用域
  </span><span style="color: rgba(136, 136, 136, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(136, 136, 136, 1)">return A::$weight;</span>这样写,会报错,提示不能通过类名访问私有静态变量  <br></span><span style="color: rgba(136, 136, 136, 1)">   //</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(136, 136, 136, 1)">weight</span> 为类的私有属性,只可在类中访问  return self::$weight;</span>
}; <br><span style="color: rgba(136, 136, 136, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"><span style="color: rgba(136, 136, 136, 1)">echo $fun();</span> 运行会报错 因为weight为私有属性 。 <br><span style="color: rgba(0, 0, 0, 1)">$weight = Closure::bind($fun,null,'A'); </span></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过bind函数作用,返回一个和$fun匿名函数一模一样的匿名函数,只是该匿名函数中A::$weight, weight属性由私有变成公有属性了。 <br><span style="color: rgba(0, 0, 0, 1)">echo $weight();</span></span></pre>
</div>
<p>为什么第二个参数又成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>综上大家应该理解其用法了吧,有时第二个参数为null,有时第三个参数可以不要,这些都跟你匿名函数里 代码中访问的方式紧密相关<br>总结:<br>1、一般匿名函数中有$this-&gt;name类似这样用 $this访问属性方式时,你在使用bind绑定时 ,第二个参数肯定要写,写出你绑定那个对象实例,第三个参数要不要呢,要看你访问的这个属性,在绑定对象中的权限属性,如果是private,protected 你要使用第三个参数 使其变为公有属性, 如果本来就是公有,你可以省略,也可以不省略<br>2、一般匿名函数中是 类名::静态属性 类似这样的访问方式(比如例子中A::$weight),你在使用bind绑定时,第二个参数可以写null,也可以写出具体的对象实例,一般写null就行(写了具体对象实例多此一举),第三个参数写不写还是得看你访问的这个静态属性的权限是 private 还是 public,如果是私有private或受保护protected的,你就得第三个参数必须写,才能使其权限变为公有属性 正常访问,如果本来就是公有public可以不用写,可以省略</p>
<p>3、需要提升属性作用域时,第3个参数需要传,传对象或者类名都可以。</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)"> Animal {
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> $cat = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">cat</span><span style="color: rgba(128, 0, 0, 1)">'</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> $dog = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">dog</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> $pig = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">pig</span><span style="color: rgba(128, 0, 0, 1)">'</span><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> $duck = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">duck</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)">不能通过 $this 访问静态变量
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">不能通过 类名::私有静态变量,只能通过self,或者static,在类里面访问私有静态变量</span>
<span style="color: rgba(0, 0, 0, 1)">
$cat </span>=<span style="color: rgba(0, 0, 0, 1)"> function() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> $<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">cat;
};

$dog </span>= <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> function () {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Animal::$dog;
};

$pig </span>=<span style="color: rgba(0, 0, 0, 1)">function() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> $<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">pig;
};

$duck </span>= <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> function() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">return Animal::$duck;这样写,会报错,提示不能通过类名访问私有静态变量</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> self::$duck; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> return static::$duck</span>
<span style="color: rgba(0, 0, 0, 1)">};

$bindCat </span>= Closure::bind($cat, <span style="color: rgba(0, 0, 255, 1)">new</span> Animal(), <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Animal</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
$bindCat2 </span>= Closure::bind($cat, <span style="color: rgba(0, 0, 255, 1)">new</span> Animal(), <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Animal());
echo $bindCat() . PHP_EOL;
echo $bindCat2() . PHP_EOL;

$bindDog </span>= Closure::bind($dog, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Animal</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
$bindDog2 </span>= Closure::bind($dog, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Animal());
echo $bindDog() . PHP_EOL;
echo $bindDog2() . PHP_EOL;

$bindPig </span>= Closure::bind($pig, <span style="color: rgba(0, 0, 255, 1)">new</span> Animal(), <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Animal</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
$bindPig2 </span>= Closure::bind($pig, <span style="color: rgba(0, 0, 255, 1)">new</span> Animal(), <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Animal());
echo $bindPig() . PHP_EOL;
echo $bindPig2() . PHP_EOL;

$bindDuck </span>= Closure::bind($duck, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Animal</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
$bindDuck2 </span>= Closure::bind($duck, <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Animal());
echo $bindDuck() . PHP_EOL;
echo $bindDuck2() . PHP_EOL;</span></pre>
</div>
<p>通过上面的例子,可以看出<strong>函数复用得,可以把函数挂在不同的类上,或者对象上</strong>。</p>
<p><strong>闭包函数的应用:</strong></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>-&gt;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>-&gt;products[$item]) ? $<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<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, &amp;<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>-&gt;<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-&gt;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>-&gt;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>-&gt;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-&gt;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><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> $attr =<span style="color: rgba(0, 0, 0, 1)"> array();
}

</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, 0, 1)"> function bind (A $a) {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> \Closure::bind(function () use ($a) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">给私有属性赋值</span>
            $a-&gt;attr =<span style="color: rgba(0, 0, 0, 1)"> [
                </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">color</span><span style="color: rgba(128, 0, 0, 1)">'</span> =&gt; <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">red</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
                </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">weight</span><span style="color: rgba(128, 0, 0, 1)">'</span> =&gt; <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">1.0</span><span style="color: rgba(128, 0, 0, 1)">'</span><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>
$a = <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-&gt;attr = ['color' =&gt; 'red','weight' =&gt; '1.0'];</span>
<span style="color: rgba(0, 0, 0, 1)">call_user_func(B::bind($a));
print_r($a);</span></pre>
</div>
<p>通过上面的几个例子,其实匿名绑定的理解就不难了....我们在看一个扩展的demo(引入trait特性)</p>
<p><strong>给类动态的添加方法</strong></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, 0, 1)"> function __call($name, $args) {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span>(is_callable($<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">$name)){ <span style="color: rgba(0, 128, 0, 1)">//getdog函数在__set已经定义和绑定了
            </span></span><span style="color: rgba(0, 0, 255, 1)">return</span> call_user_func($<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">$name, $args); <span style="color: rgba(0, 128, 0, 1)">//把第一个参数作为回调函数调用</span>
      }</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(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Method {$name} does not exist</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)">*
   * 添加方法
   </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 __set($name, $value) {
      $</span><span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;$name = is_callable($value)?<span style="color: rgba(0, 0, 0, 1)">$value</span>-&gt;bindTo($<span style="color: rgba(0, 0, 255, 1)">this</span>, $<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">):$value;<br>      <span style="color: rgba(0, 128, 0, 1)">//相当于 $getdog = function(){ return $this-&gt;dog; }<code class="php plain">Closure::bindto(</code><code class="php variable">$getdog</code><code class="php plain">, new Animal,</code><code class="php string">'Animal'</code><code class="php plain">);</code></span></span></pre>
<pre></pre>
<pre><span style="color: rgba(0, 0, 0, 1)">  } <br>} <br></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> <br><br><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Animal { <br>  use DynamicTrait; <br></span><span style="color: rgba(0, 0, 255, 1)">  private</span> $dog = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">汪汪队</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">; <br>} <br>$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> <br>$animal-&gt;getdog =<span style="color: rgba(0, 0, 0, 1)"> function() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> $<span style="color: rgba(0, 0, 255, 1)">this</span>-&gt;<span style="color: rgba(0, 0, 0, 1)">dog; }; <br>echo $animal</span>-&gt;getdog();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">输出 汪汪队</span></pre>
</div>
<p>获取类属性:第3个参数 score 范围 设置为null 时,只能获取到 public 属性。</p>
<p>如果希望完全取消绑定,则需要将闭包和范围都设置为null</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> $foo = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> $bar = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">b</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> $baz = <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">c</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)">*
   * @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> function toArray($all = <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>
      $callback =<span style="color: rgba(0, 0, 0, 1)"> (function ($obj) {
            </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, 0, 0, 1)"> get_object_vars($obj);
      });

      </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(0, 0, 0, 1)"> ($all) {
            $callback </span>= $callback-&gt;bindTo(<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">); <span style="color: rgba(0, 128, 0, 1)">//只能在类里面通过self,或者static 访问私有静态变量</span>
      } </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>
            $callback = $callback-&gt;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> $callback($<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
    }
}</span></pre>
<p>$obj = new MyClass;<br>$vars = get_object_vars($obj); // get_object_vars 类外使用,获取到的是 public 属性<br>print_r($vars);<br>echo '&lt;br/&gt;';<br>$vars = $obj-&gt;toArray();<br>print_r($vars);<br>echo '&lt;br/&gt;';<br>$vars = $obj-&gt;toArray(1);<br>print_r($vars);</p>
<pre><span style="color: rgba(0, 0, 0, 1)">&nbsp;</span></pre>
</div>
<p>结果:</p>
<div class="cnblogs_code">
<pre>Array ( =&gt;<span style="color: rgba(0, 0, 0, 1)"> a )
Array ( </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> a )
Array ( </span>=&gt; a =&gt; b =&gt; c )</pre>
</div>
<p>&nbsp;</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>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/xiangshihua/p/13282119.html
頁: [1]
查看完整版本: PHP Closure(闭包)类详解