2024 日本 PHP 研討會

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 的非靜態閉包將變為靜態閉包,而賦予非空實例但沒有作用域的非靜態閉包將被賦予未指定的類別作用域。

注意:

如果您只想複製匿名函式,可以使用複製

參數

newThis

要將給定匿名函式綁定到的物件,或者使用 null 來解除閉包的綁定。

newScope

要將閉包關聯到的類別作用域,或者使用 'static' 保持目前的作用域。如果給定一個物件,則將使用該物件的類型。這決定了綁定物件的 protected 和 private 方法的 visibility。不允許傳遞內部類別(的物件)作為此參數。

返回值

返回新建立的 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

另請參閱

新增註記

使用者貢獻的註記 8 則註記

Nezar Fadle
9 年前
我們可以使用 bindTo 的概念來編寫一個非常小的樣板引擎

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

<?php

類別 文章{
私有
$標題 = "這是一篇文章";
}

類別
貼文{
私有
$標題 = "這是一則貼文";
}

類別
樣板{

函式
渲染($上下文, $樣板檔案){

$閉包 = function($樣板檔案){
ob_start();
include
$樣板檔案;
return
ob_end_flush();
};

$閉包 = $閉包->bindTo($上下文, $上下文);
$閉包($樣板檔案);

}

}

$文章 = new 文章();
$貼文 = new 貼文();
$樣板 = new 樣板();

$樣板->渲染($文章, 'tpl.php');
$樣板->渲染($貼文, 'tpl.php');
?>

#############
tpl.php
############
<h1><?php echo $this->標題;?></h1>
tatarynowicz at gmail dot com
11 年前
您可以使用閉包綁定對物件執行類似 Javascript 的操作。

<?php
trait 動態定義 {

public function
__call($name, $args) {
if (
is_callable($this->$name)) {
return
call_user_func($this->$name, $args);
}
else {
throw new
\RuntimeException("方法 {$name} 不存在");
}
}

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

class
Foo {
use
動態定義;
private
$privateValue = 'I am private';
}

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

// 顯示 'I am private'
print $foo->bar();

?>
safakozpinar at gmail dot com
12 年前
如果您設定「newscope」參數(如手冊所述),則可以存取私有/受保護的成員。

<?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
匿名
6 年前
如果要完全解除閉包和作用域的綁定,你需要將兩者都設為 null。

<?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 用作作用域。
luc at s dot illi dot be
8 年前
存取父類別的私有成員;作用域的運用
<?PHP
類別 祖父母{ 私有 $__status1 = '已婚'; }
類別
父母 延伸 祖父母{ 私有 $__status2 = '離婚'; }
類別
延伸 父母{ 私有 $__status3 = '單身'; }

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

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

// 測試 1:
$c = $status1_3->bindTo($R = new , 父母::class);
#$c(); // 致命錯誤:無法存取私有屬性 我::$__status3

// 測試 2:
$d = $status1_2->bindTo($R = new , 父母::class);
$d();
var_dump($R);
/*
object(我)#5 (4) {
["__status3":"我":private]=>
string(6) "單身"
["__status2":"父母":private]=>
string(5) "快樂"
["__status1":"祖父母":private]=>
string(7) "已婚"
["__status1"]=>
string(5) "快樂"
}
*/

// 測試 3:
$e = $status1_3->bindTo($R = new , 祖父母::class);
#$e(); // 致命錯誤:無法存取私有屬性 我::$__status3

// 測試 4:
$f = $status1_2->bindTo($R = new , 祖父母::class);
$f();
var_dump($R);
/*
object(我)#9 (4) {
["__status3":"我":private]=>
string(6) "單身"
["__status2":"父母":private]=>
string(8) "離婚"
["__status1":"祖父母":private]=>
string(5) "快樂"
["__status2"]=>
string(5) "快樂"
}
*/
?>

清除堆疊追蹤 (清除堆疊軌跡)
<?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) {
}
*/
?>
amica at php-resource dot de
12 年前
利用可重新綁定的 $this 可以做一些邪惡的事情

<?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
?>
malferov at gmail dot com
1 年前
如果您像我一樣,沒有立即理解文件中關於 `newScope` 參數的「(一個)內部類別的(物件)」究竟是什麼意思

文件中所說的內部類別,指的是任何 PHP 內建類別,例如 `stdClass`、`Closure`、`WeakMap` 等等。

<?php

類別 A {}
$a = new A();
$closure = fn() => null;

$binded = $closure->bindTo($a, 'stdClass',); // 無法將閉包綁定到內建類別 stdClass 的作用域
$binded = $closure->bindTo($a, $closure,); // 警告:無法將閉包綁定到內建類別 Closure 的作用域等等
Olexandr Kalaidzhy
2 年前
取得所有物件變數,不使用 Reflection

<?php

declare(strict_types=1);

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

函式
get_object_vars_all($object): array
{
if (!
\is_object($object)) {
throw new
\InvalidArgumentException(sprintf('引數應該是一個物件,給定 "%s"。', 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));

?>

輸出

陣列(1) {
["buz"]=>
字串(3) "buz"
}
陣列(3) {
["foo"]=>
字串(3) "foo"
["bar"]=>
字串(3) "bar"
["buz"]=>
字串(3) "buz"
}
To Top