We can use the concept of bindTo to write a very small Template Engine:
#############
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><?php echo $this->title;?></h1>Closure::bindTo
Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.
Closure::bindTo
Референца за `closure.bindto.php` со подобрена типографија и навигација.
Closure::bindTo
(PHP 5 >= 5.4.0, PHP 7, PHP 8)
Closure::bindTo — (PHP 5 >= 5.4.0, PHP 7, PHP 8)
= NULL
$newThis, object|string|null $newScope (PHP 5 >= 5.3.0, PHP 7, PHP 8)): ?ЗатворањеКонвертира повикувач во затворање анонимна функција Ја дуплира затвореноста со нов поврзан објект и класен опсег
со истото тело и поврзани променливи како оваа, но со можност за различен поврзан објект и нов класен опсег. $this „Поврзаниот објект“ ја одредува вредноста newScope
parameter.
ќе има во телото на функцијата и „класниот опсег“ претставува класа која одредува кои приватни и заштитени членови ќе може да ги пристапи анонимната функција. Имено, членовите што ќе бидат видливи се исти како да беше анонимната функција метод на класата дадена како вредност на
newThis Статичните затворености не можат да имаат поврзан објект (вредноста на параметарот nullтреба да биде
), но овој метод сепак може да се користи за промена на нивниот класен опсег. null Овој метод ќе осигури дека за нестатична затвореност, имањето поврзан примерок ќе подразбира да биде со опсег и обратно. За таа цел, нестатичните затворености на кои им е даден опсег, но
Забелешка:
примерок се прават статични и нестатични затворености без опсег на кои им е даден примерок што не е null се со опсег до неопределена класа. cloning instead.
Параметри
newThis-
Ако сакате само да ги дуплирате анонимните функции, можете да го користите
nullОбјектот на кој треба да биде поврзана дадената анонимна функција, или newScope-
за затвореноста да биде неповрзана.
Примери
Пример #1 Класниот опсег на кој треба да биде поврзана затвореноста, или 'static' за да се задржи сегашниот. Ако е даден објект, наместо тоа ќе се користи типот на објектот. Ова ја одредува видливоста на заштитените и приватните методи на поврзаниот објект. Не е дозволено да се помине (објект од) внатрешна класа како овој параметар. example
<?php
class A
{
private $val;
public function __construct($val)
{
$this->val = $val;
}
public function getClosure()
{
// Returns closure bound to this object and scope
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
Види Исто така
- Анонимни функции
- Closure::bindTo() Closure::bind()
Белешки од корисници 8 белешки
You can do pretty Javascript-like things with objects using closure binding:
<?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;
};
// prints 'I am private'
print $foo->bar();
?>Private/protected members are accessible if you set the "newscope" argument (as the manual says).
<?php
$fn = function(){
return ++$this->foo; // increase the value
};
class Bar{
private $foo = 1; // initial value
}
$bar = new Bar();
$fn1 = $fn->bindTo($bar, 'Bar'); // specify class name
$fn2 = $fn->bindTo($bar, $bar); // or object
echo $fn1(); // 2
echo $fn2(); // 3If you want to unbind completely the closure and the scope you need to set both to null:
<?php
class MyClass
{
public $foo = 'a';
protected $bar = 'b';
private $baz = 'c';
/**
* @return array
*/
public function toArray()
{
// Only public variables
return (function ($obj) {
return get_object_vars($obj);
})->bindTo(null, null)($this);
}
}
?>
In this example, only the public variables of the class are exported (foo).
If you use the default scope (->bindTo(null)) also protected and private variables are exported (foo, bar and baz).
It was hard to figure it out because there is nowhere mentioned in the documentation that you can use null as a scope.Access private members of parent classes; playing with the scopes:
<?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';
};
// test 1:
$c = $status1_3->bindTo($R = new Me, Parents::class);
#$c(); // Fatal: Cannot access private property Me::$__status3
// test 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"
}
*/
// test 3:
$e = $status1_3->bindTo($R = new Me, Grandparents::class);
#$e(); // Fatal: Cannot access private property Me::$__status3
// test 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"
}
*/
?>
Clear the stack trace:
<?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) {
}
*/
?>With rebindable $this at hand it's possible to do evil stuff:
<?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
?>If you, like me, did not immediately understand what exactly "(an object of) an internal class" in the documentation about the 'newScope' parameter:
By an internal class, the documentation means any internal PHP class such as 'stdClass', 'Closure', 'WeakMap', and etc:
<?php
class A {}
$a = new A();
$closure = fn() => null;
$binded = $closure->bindTo($a, 'stdClass',); // Cannot bind closure to scope of internal class stdClass
$binded = $closure->bindTo($a, $closure,); // Warning: Cannot bind closure to scope of internal class Closure etc.Get all object vars without using Reflection:
<?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));
?>
The output:
array(1) {
["buz"]=>
string(3) "buz"
}
array(3) {
["foo"]=>
string(3) "foo"
["bar"]=>
string(3) "bar"
["buz"]=>
string(3) "buz"
}