Closure::bindTo

(PHP 5 >= 5.4.0, PHP 7, PHP 8)

Closure::bindTo 使用新的绑定对象和类范围复制闭包

说明

public Closure::bindTo(?object $newThis, object|string|null $newScope = "static"): ?Closure

创建并返回一个新的 匿名函数,它与当前闭包具有相同的代码体和绑定变量,但可能绑定了不同的对象,并具有新的类范围。

“绑定对象”决定 $this 在函数体中的值,而“类范围”代表一个类,该类决定匿名函数可以访问哪些私有和受保护的成员。也就是说,可见的成员与匿名函数是 newScope 参数值的类的方法时相同。

静态闭包不能绑定任何对象(newThis 参数的值应为 null),但此函数仍然可以用于更改其类范围。

此函数将确保,对于非静态闭包,绑定实例意味着有范围,反之亦然。为此,具有范围但实例为 null 的非静态闭包将变为静态,而没有范围但实例不为 null 的非静态闭包将被作用域到一个未指定的类。

注意:

如果您只想复制匿名函数,可以使用 克隆 代替。

参数

newThis

要绑定的匿名函数的对象,或 null 用于将闭包解绑。

newScope

要关联闭包的类范围,或 'static' 保持当前范围。如果传递了一个对象,则将使用该对象的类型。这决定了绑定对象中受保护和私有方法的可见性。不允许传递(内部类的)对象作为此参数。

返回值

返回新创建的 Closure 对象,或者在失败时返回 null

范例

示例 #1 Closure::bindTo() 范例

<?php

class A
{
private
$val;

public function
__construct($val)
{
$this->val = $val;
}

public function
getClosure()
{
// 返回绑定到此对象和范围的闭包
return function() {
return
$this->val;
};
}
}

$ob1 = new A(1);
$ob2 = new A(2);

$cl = $ob1->getClosure();
echo
$cl(), "\n";

$cl = $cl->bindTo($ob2);
echo
$cl(), "\n";

?>

上面的示例将输出类似于以下的内容

1
2

参见

添加说明

用户贡献说明 9 说明

36
tatarynowicz at gmail dot com
11 年前
您可以使用闭包绑定来完成类似 JavaScript 的对象操作

<?php
trait DynamicDefinition {

public function
__call($name, $args) {
if (
is_callable($this->$name)) {
return
call_user_func($this->$name, $args);
}
else {
throw new
\RuntimeException("Method {$name} does not exist");
}
}

public function
__set($name, $value) {
$this->$name = is_callable($value)?
$value->bindTo($this, $this):
$value;
}
}

class
Foo {
use
DynamicDefinition;
private
$privateValue = 'I am private';
}

$foo = new Foo;
$foo->bar = function() {
return
$this->privateValue;
};

// 打印 'I am private'
print $foo->bar();

?>
43
Nezar Fadle
9 年前
我们可以使用 bindTo 的概念编写一个非常小的模板引擎

#############
index.php
############

<?php

class Article{
private
$title = "This is an article";
}

class
Post{
private
$title = "This is a post";
}

class
Template{

function
render($context, $tpl){

$closure = function($tpl){
ob_start();
include
$tpl;
return
ob_end_flush();
};

$closure = $closure->bindTo($context, $context);
$closure($tpl);

}

}

$art = new Article();
$post = new Post();
$template = new Template();

$template->render($art, 'tpl.php');
$template->render($post, 'tpl.php');
?>

#############
tpl.php
############
<h1><span class="default"><?php
echo $this->title;?></h1>
20
<a href="#107848" class="name"> <strong class="user"><em>safakozpinar at gmail dot com</em></strong></a><a class="genanchor" href="#107848"> ¶</a>
12 年前
如果您设置了“newscope”参数(如手册中所述),则可以访问私有/受保护的成员。

<span class="default"><?php
$fn
= function(){
return ++
$this->foo; // 增加值
};

class
Bar{
private
$foo = 1; // 初始值
}

$bar = new Bar();

$fn1 = $fn->bindTo($bar, 'Bar'); // 指定类名
$fn2 = $fn->bindTo($bar, $bar); // 或者对象

echo $fn1(); // 2
echo $fn2(); // 3
5
<a href="#122641" class="name"> <strong class="user"><em>Anonymous</em></strong></a><a class="genanchor" href="#122641"> ¶</a>
6 年前
如果您想完全解除闭包和作用域的绑定,您需要将两者都设置为 null

<span class="default"><?php
class MyClass
{
public
$foo = 'a';
protected
$bar = 'b';
private
$baz = 'c';

/**
* @return array
*/
public function toArray()
{
// 仅公共变量
return (function ($obj) {
return
get_object_vars($obj);
})->
bindTo(null, null)($this);
}
}
?>

在这个例子中,只导出了类的公共变量(foo)。

如果您使用默认范围 (->bindTo(null)),那么也会导出受保护和私有变量(foo、bar 和 baz)。

很难弄清楚这一点,因为文档中没有提到您可以将 null 用作范围。
3
<a href="#119410" class="name"> <strong class="user"><em>luc at s dot illi dot be</em></strong></a><a class="genanchor" href="#119410"> ¶</a>
8 年前
访问父类的私有成员;玩弄作用域
<span class="default"><?PHP
class Grandparents{ private $__status1 = 'married'; }
class
Parents extends Grandparents{ private $__status2 = 'divorced'; }
class
Me extends Parents{ private $__status3 = 'single'; }

$status1_3 = function()
{
$this->__status1 = 'happy';
$this->__status2 = 'happy';
$this->__status3 = 'happy';
};

$status1_2 = function()
{
$this->__status1 = 'happy';
$this->__status2 = 'happy';
};

// 测试 1:
$c = $status1_3->bindTo($R = new Me, Parents::class);
#$c(); // 致命错误:无法访问私有属性 Me::$__status3

// 测试 2:
$d = $status1_2->bindTo($R = new Me, Parents::class);
$d();
var_dump($R);
/*
object(Me)#5 (4) {
["__status3":"Me":private]=>
string(6) "single"
["__status2":"Parents":private]=>
string(5) "happy"
["__status1":"Grandparents":private]=>
string(7) "married"
["__status1"]=>
string(5) "happy"
}
*/

// 测试 3:
$e = $status1_3->bindTo($R = new Me, Grandparents::class);
#$e(); // 致命错误:无法访问私有属性 Me::$__status3

// 测试 4:
$f = $status1_2->bindTo($R = new Me, Grandparents::class);
$f();
var_dump($R);
/*
object(Me)#9 (4) {
["__status3":"Me":private]=>
string(6) "single"
["__status2":"Parents":private]=>
string(8) "divorced"
["__status1":"Grandparents":private]=>
string(5) "happy"
["__status2"]=>
string(5) "happy"
}
*/
?>

清除堆栈跟踪
<span class="default"><?PHP
use Exception;
use
ReflectionException;

$c = function()
{
$this->trace = [];
};

$c = $c->bindTo($R = new ReflectionException, Exception::class);
$c();

try
{
throw
$R;
}
catch(
ReflectionException $R)
{
var_dump($R->getTrace());
}
/*
array(0) {
}
*/
?>
8
<a href="#106912" class="name"> <strong class="user"><em>amica at php-resource dot de</em></strong></a><a class="genanchor" href="#106912"> ¶</a>
12 年前
有了可重新绑定的 $this,就可以做一些邪恶的事情

<span class="default"><?php
class A {
private
$a = 12;
private function
getA () {
return
$this->a;
}
}
class
B {
private
$b = 34;
private function
getB () {
return
$this->b;
}
}
$a = new A();
$b = new B();
$c = function () {
if (
property_exists($this, "a") && method_exists($this, "getA")) {
$this->a++;
return
$this->getA();
}
if (
property_exists($this, "b") && method_exists($this, "getB")) {
$this->b++;
return
$this->getB();
}
};
$ca = $c->bindTo($a, $a);
$cb = $c->bindTo($b, $b);
echo
$ca(), "\n"; // => 13
echo $cb(), "\n"; // => 35
?>
1
<a href="#129057" class="name"> <strong class="user"><em>malferov at gmail dot com</em></strong></a><a class="genanchor" href="#129057"> ¶</a>
8 个月前
如果您像我一样,没有立即理解关于 'newScope' 参数的文档中“内部类的(一个)对象”到底是什么

文档是关于闭包本身的对象

<span class="default"><?php

class A {}

$a = new A();

$closure = fn() => null;

$binded = $closure->bindTo($a, $closure,); // 警告:无法将闭包绑定到内部类 Closure 的范围
0
<a href="#126847" class="name"> <strong class="user"><em>Olexandr Kalaidzhy</em></strong></a><a class="genanchor" href="#126847"> ¶</a>
2 年前
获取所有对象变量,无需使用反射

<?php

declare(strict_types=1);

class
A
{
private
$foo = 'foo';
protected
$bar = 'bar';
public
$buz = 'buz';
}

function
get_object_vars_all($object): array
{
if (!
\is_object($object)) {
throw new
\InvalidArgumentException(sprintf('The argument should be an object, "%s" given.', get_debug_type($object)));
}

$closure = function () {
return
get_object_vars($this);
};

return
$closure->bindTo($object, $object)();
}

$a = new A();

var_dump(get_object_vars($a));
var_dump(get_object_vars_all($a));

?>

输出

array(1) {
["buz"]=>
string(3) "buz"
}
array(3) {
["foo"]=>
string(3) "foo"
["bar"]=>
string(3) "bar"
["buz"]=>
string(3) "buz"
}
-32
anthony bishopric
12 年前
闭包可以重新绑定它们的 $this 变量,但 $this 的私有/受保护的方法和函数无法被闭包访问。

<?php
$fn
= function(){
return
$this->foo;
};

class
Bar{
private
$foo = 3;
}

$bar = new Bar();

$fn = $fn->bindTo($bar);

echo
$fn(); // Fatal error: Cannot access private property Bar::$foo
To Top