alpa / tools_proxy_object
创建一个代理对象,并在访问对象成员时分配获取/设置处理程序。
Requires
- php: ^7.4||^8
Requires (Dev)
- phpunit/phpunit: ^9.5
README
安装
composer require alpa/tools_proxy_object:1.0.14
变更
- v1.0.12 增加了通过引用返回结果的能力(对于操作:get、call、invoke)- 警告:没有向后兼容性
- v1.0.13 增加了在通过引用传递前的属性初始化检查- 兼容上一个版本
- v1.0.14 修复了错误:只有变量引用应该通过引用返回(或分配)。- 警告:没有向后兼容性。get操作、call操作、invoke操作现在需要通过引用声明(
function & (){}
),并通过变量返回结果。现在有一个抽象类用于创建新的代理。创建了接口以实现依赖倒置。
描述
代理对象是一个实现了所有可用魔术方法的对象,这些方法运行相应的处理程序。这些处理程序在目标对象上执行一系列自定义操作。
基本上,这种行为在访问目标对象的属性或方法时处理结果时是必要的。
简单来说
- 在获取/写入目标对象属性的值之前,必须在沙盒中处理。
- 在执行目标对象方法之前,需要先在沙盒中处理。
该组件为观察对象或类创建代理对象。
为可观察对象或类中的每个成员分配操作处理程序(get | set | call | isset | unset | invoke | toString | iterator)。
JavaScript中通过Proxy构造函数实现了类似的原则。
通过代理对象访问对象成员时,将调用为特定操作分配的处理程序。
组件可以应用的地方
- 数据验证的中介
- 通过反射访问对象的私有数据
- 动态数据构建,以及其他属性的生成
- 动态数据请求,例如从数据库
- 其他选项
注意
1
从版本1.0.0到版本1.1.0是实验性开发,其中API可能会进行修改。
有关版本兼容性,请参阅GitHub上的标签描述。
将组件包含到项目中时,请指定特定版本。
特定版本的API描述可以在提交版本中找到。
2
如果您由于某些原因遇到错误或其他问题,建议您实现自己的处理程序类来解决这个问题。请参阅 - 创建处理程序类
3
代理对象是一个实现魔术方法的包装对象。默认情况下,无法访问可观察对象/类的受保护/私有成员。默认代理仅与可观察/类的公共成员一起工作。如果您需要能够处理对象/类的受保护/私有成员,请在使用操作处理程序时使用Reflector类。
4
代理对象的使用不一定快。但无论如何,这是创建复杂组合的最佳解决方案,并且简单易用。
安装
composer require alpa/tools_proxy_object
入门
示例1
<?php use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\Instance { protected static function & static_get ( $target,string $prop,$val_or_args,ProxyInterface $proxy) { $answer=is_string($target->$prop) ? strtoupper($target->$prop) : $target->$prop; return $answer; //Only variables are returned by reference } protected static function & static_get_test( $target,string $prop,$val_or_args,ProxyInterface $proxy) { $answer = is_string($target->$prop) ? strtolower($target->$prop) : $target->$prop; return $answer ; //Only variables are returned by reference } } $obj=(object)[ 'test'=>'HELLO', 'other'=>'bay' ]; $proxy=new Proxy($obj,MyHandlers::class); echo $proxy->test; // hello echo $proxy->other;// BAY
示例2
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\Instance { public function __construct($prefix) { $this->prefix=$prefix; } protected function & get( $target,string $prop,$val_or_args,ProxyInterface $proxy) { $answer = is_string($target->$prop) ? strtoupper($this->prefix.$target->$prop) : $target->$prop; return $answer; //Only variables are returned by reference } protected function & get_test($target,string $prop,$val_or_args,ProxyInterface $proxy) { $answer = is_string($target->$prop) ? strtolower($this->prefix.$target->$prop) : $target->$prop; return $answer; //Only variables are returned by reference } } $inst=new MyHandlers('Alex '); $obj=(object)[ 'test'=>'HELLO', 'other'=>'bay' ]; $proxy=new Proxy($obj,$inst); //or $proxy=$inst::proxy($obj,$inst); // or $proxy=new Proxy($obj,$inst); echo $proxy->test; // alex hello echo $proxy->other;// ALEX BAY
示例3
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; $handlers = new Handlers\Closures(); $handlers->init('get',function & ($target,$prop,ProxyInterface $proxy){ $answer= is_string($target->$prop) ? strtoupper($target->$prop) : $target->$prop; return $answer; //Only variables are returned by reference }); $handlers->initProp('get','test',function & ($target,$prop, ProxyInterface $proxy){ $answer = is_string($target->$prop) ? strtolower($target->$prop) : $target->$prop; return $answer; //Only variables are returned by reference }); $obj=(object)[ 'test'=>'HELLO', 'other'=>'bay' ]; $proxy=new Proxy($obj,$handlers); echo $proxy->test; // hello echo $proxy->other;// BAY
示例4 -
<?php use \Alpa\Tools\ProxyObject\Proxy; use \Alpa\Tools\ProxyObject\ProxyInterface; use \Alpa\Tools\ProxyObject\Handlers; $handlers = new Handlers([ 'get' => function & ($target, $name, ProxyInterface $proxy) { $name = '_' . $name; return $target->$name; }, 'set' => function ($target, $name, $value, ProxyInterface $proxy): void { $name = '_' . $name; $target->$name = $value; }, 'isset' => function ($target, $name, ProxyInterface $proxy): bool { $name = '_' . $name; return property_exists($target,$name) ; }, 'unset' => function ($target, $name, ProxyInterface $proxy): void { $name = '_' . $name; unset($target->$name); }, 'iterator' => function ($target, $proxy) { return new class($target, $proxy) implements \Iterator { private object $target; private Proxy $proxy; private array $keys = []; private int $key = 0; public function __construct(object $target, ProxyInterface $proxy) { $this->target = $target; $this->proxy = $proxy; $this->rewind(); } public function current() { $prop = $this->key(); return $prop !== null ? $this->proxy->$prop : null; } public function key() { $prop = $this->keys[$this->key] ?? null; return $prop !== null ? ltrim($prop, '_') : null; } public function next(): void { $this->key++; } public function rewind() { $this->key = 0; $this->keys = array_keys(get_object_vars($this->target)); } public function valid(): bool { $prop = $this->key(); return $prop !== null && isset($this->proxy->$prop); } }; } ]); $target=(object)['_test'=>'test']; $proxy=new Proxy($target,$handlers); echo $proxy->test;// get $target->_test value return 'test' $proxy->test='new value';// set $target->_test value echo $proxy->test; // get $target->_test value return 'new value' echo isset($proxy->test); // isset($target->_test) return true foreach($proxy as $key=>$value){ echo $key; // test echo $value; // $proxy->test value => $target->_test value } unset($proxy->test); // unset($target->_test) echo key_exists($target,'_test'); // return false;
定义
定义
- 成员 - 对象或类的属性和方法
- 动作(s)- 可以应用于类或对象成员的动作(
set|get|isset|unset|call
)。以及应用于对象或类的动作(invoke | toString |iterator
)。有时,“动作”的定义被视为动作处理器。 - 处理器(s)或动作处理器 - 处理动作的函数或方法
- 代理 - 具有声明魔术方法的对象,它将通过自身传递动作到观察对象或类的成员。代理对象是一个实现魔术方法的包装对象。默认代理仅与观察对象/类的公共成员一起工作。
为代理对象创建处理器
编写处理器有两种方式
- 通过闭包函数动态编写处理器。
- 通过类声明编写处理器。
有两种类型的处理器
- 对象特定成员的处理器;
- 对象所有成员的处理器;
如果动作处理器未分配给成员,则应用所有成员的动作处理器。
如果动作处理器未分配给成员,则应用标准动作。
在访问对象成员时存在以下动作
- set - 成员值输入;
- get - 成员值查询;
- isset - 成员检查;
- unset - 成员删除;
- call - 成员调用;
- invoke - 调用对象或类;
- toString - 将对象或类转换为字符串;
- iterator - 在遍历对象成员时分配迭代器。
通过闭包函数动态编写处理器
构造函数中的示例
<?php $handlers=new \Alpa\Tools\ProxyObject\Handlers\Closures([ // handler for members query 'get'=>function &($target,$prop,$proxy){}, // handler for members entry 'set'=>function($target,$prop,$value,$proxy):void{}, // handler for entry members 'unset'=>function($target,$prop,$proxy):void{}, // handler to check if members exist 'isset'=>function($target,$prop,$proxy):bool{}, // handler to call members 'call'=>function & ($target,$prop,$args,$proxy){}, // handler for invoke object or class 'invoke'=>function & ($target,array $args,$proxy){}, // handler for toString object or class 'toString'=>function($target,$proxy):string {}, // handler for delete members 'iterator'=>function($target,$prop,$proxy):\Traversable{}, ]);
处理器可以在构造函数外部分配。
通过Handlers :: init方法分配处理器的示例
<?php $handlers=new \Alpa\Tools\ProxyObject\Handlers\Closures(); $handlers->init('get',function & ($target,$name,$proxy){}); $handlers->init('set',function($target,$name,$value,$proxy):void{}); $handlers->init('unset',function($target,$prop,$proxy):void{}); $handlers->init('isset',function($target,$prop,$proxy):bool{}); $handlers->init('call',function & ($target,$prop, $args,$proxy){}); $handlers->init('invoke',function & ($target,$args,$proxy){}); $handlers->init('toString',function($target,$proxy){}); $handlers->init('iterator',function($target,$prop,$proxy):\Traversable{});
为特定属性分配处理器的示例
<?php $handlers=new \Alpa\Tools\ProxyObject\Handlers\Closures([],[ 'get'=>[ 'prop'=>function & ($target,$name,$proxy):mixed{} ], 'set'=>[ 'prop'=>function ($target,$name,$value,$proxy):void{} ], 'unset'=>[ 'prop'=>function ($target,$name,$proxy):void{} ] , 'isset'=>[ 'prop'=>function ($target,$name,$proxy):bool{} ], 'call'=>[ 'prop'=>function & ($target,$name,$args,$proxy){} ] ]);
或
<?php $handlers=new \Alpa\Tools\ProxyObject\Handlers\Closures(); $handlers->initProp('get','prop',function & ($target,$name,$proxy):mixed{}); $handlers->initProp('set','prop',function ($target,$name,$value,$proxy):void{}); $handlers->initProp('unset','prop',function ($target,$name,$proxy):void{}); $handlers->initProp('isset','prop',function ($target,$name,$proxy):bool{}); $handlers->initProp('call','prop',function & ($target,$name,$args,$proxy){});
通过类声明静态编写处理器。
其中方法将是处理器的类声明。
<?php use Alpa\Tools\ProxyObject\Handlers\Instance; class MyHandlers extends Instance { };
或
<?php // to declare only static actions use Alpa\Tools\ProxyObject\Handlers\StaticActions; class MyHandlers extends StaticActions { };
或
<?php // to declare only instance actions use Alpa\Tools\ProxyObject\Handlers\InstanceActions; class MyHandlers extends InstanceActions { };
可以声明以下实例方法作为处理器(当继承类 Alpa\ProxyObject\Handlers\StaticActions
或 Alpa\ProxyObject\Handlers\InstanceActions
或 Alpa\ProxyObject\Handlers\Instance
时)
- get - 成员值查询;
- get_{$name_property} - 查询名为 $name_property 的成员的值;
- set - 成员值输入;
- set_{$name_property} - 输入名为 $name_property 的成员的值;
- isset - 检查是否设置了成员;
- isset_{$name_property} - 检查是否设置了名为 $name_property 的成员;
- unset - 删除成员;
- unset_{$name_property} - 删除名为 $name_property 的成员;
- call - 调用成员;
- call_{$name_property} - 调用名为 $name_property 的成员;
- invoke - 调用对象或类;
- toString - 将对象或类转换为字符串;
- iterator - 为 foreach 分配迭代器;
可以声明以下静态方法作为处理器:(当继承类 Alpa\ProxyObject\Handlers\Instance
时)
- static_get - 成员值查询;
- static_get_{$name_property} - 查询名为 $name_property 的成员的值;
- static_set - 成员值输入;
- static_set_{$name_property} - 输入名为 $name_property 的成员的值;
- static_isset - 检查是否设置了成员;
- static_isset_{$name_property} - 检查是否设置了名为 $name_property 的成员;
- static_unset - 删除成员;
- static_unset_{$name_property} - 删除名为 $name_property 的成员;
- static_call - 调用成员;
- static_call_{$name_property} - 调用名为 $name_property 的成员;
- static_invoke - 调用对象或类;
- static_toString - 将对象或类转换为字符串;
- static_iterator - 为 foreach 分配迭代器;
创建对象所有成员的动作处理器模板。
声明静态动作和实例动作的模板
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\Instance { /** * member value query handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param Proxy $proxy - the proxy object from which the method is called * @return mixed - it is necessary to return the result */ protected function & get ($target,string $prop,$value_or_args,ProxyInterface $proxy) { return parent::get($target,$prop,$value_or_args,$proxy); } /** * member value entry handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param mixed $value_or_args - value to assign * @param Proxy $proxy - the proxy object from which the method is called * @return void */ protected function set ( $target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::set($target,$prop,$value_or_args,$proxy); } /** * checking is set member handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return bool */ protected function isset ($target,string $prop,$value_or_args,ProxyInterface $proxy):bool { return parent::isset($target,$prop,$value_or_args,$proxy); } /** * member delete handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return void */ protected function unset ($target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::unset($target,$prop,$value_or_args,$proxy); } /** * Member call handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param array $value_or_args - arguments to the called function. * @param Proxy $proxy the proxy object from which the method is called * @return mixed */ protected function & call ($target,string $prop,array $value_or_args,ProxyInterface $proxy) { return parent::call($target,$prop,$value_or_args,$proxy); } /** * invoke object * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop -irrelevant * @param array $value_or_args - arguments to the called function. * @param Proxy $proxy the proxy object from which the method is called * @return mixed */ protected function & invoke($target, $prop, array $value_or_args, ProxyInterface $proxy) { return parent::invoke($target,$prop,$value_or_args,$proxy); } /** * converting to string object or class * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop -irrelevant * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return string */ protected function toString($target, $prop, $value_or_args, ProxyInterface $proxy):string { return parent::toString($target,$prop,$value_or_args,$proxy); } /** * creates an iterator for foreach * @param object|string $target - observable object or class. * @param null $prop - irrelevant * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return \Traversable */ protected function iterator ($target,$prop,$value_or_args,ProxyInterface $proxy):\Traversable { return parent::iterator($target,$prop,$value_or_args,$proxy); } /** * member value query handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param Proxy $proxy - the proxy object from which the method is called * @return mixed - it is necessary to return the result */ protected static function & static_get ($target,string $prop,$value_or_args,Proxy $proxy) { return parent::static_get($target,$prop,$value_or_args,$proxy); } /** * member value entry handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param mixed $value_or_args - value to assign * @param Proxy $proxy - the proxy object from which the method is called * @return void */ protected static function static_set ($target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::static_set($target,$prop,$value_or_args,$proxy); } /** * checking is set member handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return bool */ protected static function static_isset ($target,string $prop,$value_or_args,ProxyInterface $proxy):bool { return parent::static_isset($target,$prop,$value_or_args,$proxy); } /** * member delete handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return void */ protected static function static_unset ($target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::static_unset($target,$prop,$value_or_args,$proxy); } /** * Member call handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param array $value_or_args - arguments to the called function. * @param Proxy $proxy the proxy object from which the method is called * @return mixed */ protected static function static_call ($target,string $prop,array $value_or_args =[],ProxyInterface $proxy) { return parent::static_call($target,$prop,$value_or_args,$proxy); } /** * invoke object * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop - object member name * @param array $value_or_args - arguments to the called function. * @param Proxy $proxy the proxy object from which the method is called * @return mixed */ protected static function & static_invoke($target, $prop, array $value_or_args, ProxyInterface $proxy) { return parent::static_invoke($target,$prop,$value_or_args,$proxy); } /** * converting to string object or class * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop -irrelevant * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return string */ protected static function static_toString($target, $prop, $value_or_args, ProxyInterface $proxy):string { return parent::static_toString($target,$prop,$value_or_args,$proxy); } /** * creates an iterator for foreach * @param object|string $target - observable object or class. * @param null $prop - irrelevant * @param null $value_or_args -irrelevant * @param Proxy $proxy the proxy object from which the method is called * @return \Traversable */ protected static function static_iterator ($target,$prop,$value_or_args,ProxyInterface $proxy):\Traversable { return parent::static_iterator($target,$prop,$value_or_args,$proxy); } };
仅声明实例动作的模板
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\InstanceActions { /** * member value query handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param ProxyInterface $proxy - the proxy object from which the method is called * @return mixed - it is necessary to return the result */ protected function & get ($target,string $prop,$value_or_args,ProxyInterface $proxy) { return parent::get($target,$prop,$value_or_args,$proxy); } /** * member value entry handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param mixed $value_or_args - value to assign * @param ProxyInterface $proxy - the proxy object from which the method is called * @return void */ protected function set ( $target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::set($target,$prop,$value_or_args,$proxy); } /** * checking is set member handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return bool */ protected function isset ($target,string $prop,$value_or_args,ProxyInterface $proxy):bool { return parent::isset($target,$prop,$value_or_args,$proxy); } /** * member delete handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return void */ protected function unset ($target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::unset($target,$prop,$value_or_args,$proxy); } /** * Member call handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param array $value_or_args - arguments to the called function. * @param ProxyInterface $proxy the proxy object from which the method is called * @return mixed */ protected function & call ($target,string $prop,array $value_or_args,ProxyInterface $proxy) { return parent::call($target,$prop,$value_or_args,$proxy); } /** * invoke object * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop - object member name * @param array $value_or_args - arguments to the called function. * @param ProxyInterface $proxy the proxy object from which the method is called * @return mixed */ protected function & invoke($target, $prop=null, array $value_or_args, ProxyInterface $proxy) { return parent::invoke($target,$prop,$value_or_args,$proxy); } /** * converting to string object or class * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop -irrelevant * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return string */ protected function toString($target, $prop, $value_or_args, ProxyInterface $proxy):string { return parent::toString($target,$prop,$value_or_args,$proxy); } /** * creates an iterator for foreach * @param object|string $target - observable object or class. * @param null $prop - irrelevant * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return \Traversable */ protected function iterator ($target,$prop,$value_or_args,ProxyInterface $proxy):\Traversable { return parent::iterator($target,$prop,$value_or_args,$proxy); } };
仅声明静态动作的模板
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\StaticActions { /** * member value query handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param ProxyInterface $proxy - the proxy object from which the method is called * @return mixed - it is necessary to return the result */ protected static function & get ($target,string $prop,$value_or_args, ProxyInterface $proxy) { return parent::get($target,$prop,$value_or_args,$proxy); } /** * member value entry handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param mixed $value_or_args - value to assign * @param ProxyInterface $proxy - the proxy object from which the method is called * @return void */ protected static function set ( $target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::set($target,$prop,$value_or_args,$proxy); } /** * checking is set member handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args - irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return bool */ protected static function isset ($target,string $prop,$value_or_args,ProxyInterface $proxy):bool { return parent::isset($target,$prop,$value_or_args,$proxy); } /** * member delete handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return void */ public static function unset ($target,string $prop,$value_or_args,ProxyInterface $proxy):void { parent::unset($target,$prop,$value_or_args,$proxy); } /** * Member call handler * @param object|string $target - observable object or class. * @param string $prop - object member name * @param array $value_or_args - arguments to the called function. * @param ProxyInterface $proxy the proxy object from which the method is called * @return mixed */ protected static function & call ($target,string $prop,array $value_or_args,ProxyInterface $proxy) { return parent::call($target,$prop,$value_or_args,$proxy); } /** * invoke object * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop - object member name * @param array $value_or_args - arguments to the called function. * @param ProxyInterface $proxy the proxy object from which the method is called * @return mixed */ protected static function & invoke($target, $prop, array $value_or_args, ProxyInterface $proxy) { return parent::static_invoke($target,$prop,$value_or_args,$proxy); } /** * converting to string object or class * by default the member in target must be a method * @param object|string $target - observable object * @param null $prop -irrelevant * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return string */ protected static function toString($target, $prop, $value_or_args, ProxyInterface $proxy):string { return parent::toString($target,$prop,$value_or_args,$proxy); } /** * creates an iterator for foreach * @param object|string $target - observable object or class. * @param null $prop - irrelevant * @param null $value_or_args -irrelevant * @param ProxyInterface $proxy the proxy object from which the method is called * @return \Traversable */ protected static function iterator ($target,$prop,$value_or_args,ProxyInterface $proxy):\Traversable { return parent::iterator($target,$prop,$value_or_args,$proxy); } };
特定成员的动作处理器创建方式与所有属性的动作处理器类似。例外是 "invoke"、"toString" 和 "iterator" 动作,它们仅适用于可观察对象或类。
示例
<?php use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Handlers; class MyHandlers extends Handlers\Instance { protected static function & static_get($target,string $prop,$val_or_args,ProxyInterface $proxy) { $answer=is_string($target->$prop)?strtoupper($target->$prop):$target->$prop; return $answer; } protected static function & static_get_test($target,string $prop,$val_or_args,ProxyInterface $proxy) { // $prop==='test'; $answer=is_string($target->$prop)?strtolower($target->$prop):$target->$prop; return $answer; } }; $obj=(object)[ 'test'=>'HELLO', 'other'=>'bay' ]; $proxy=new Proxy($obj,MyHandlers::class); echo $proxy->test; // hello echo $proxy->other;// BAY
代理类静态成员
除了代理对象成员外,还可以代理类的静态成员。为此,需要指定类而不是对象。
<?php use Alpa\ProxyObject\Handlers\Instance; class MyClass{ public static $prop1='Hello'; public static $prop2='bay'; public static function method(int $arg) { return $arg+1; } } class MyHandlers extends Instance{} $proxy = new Proxy(MyClass::class,MyHandlers::class); echo $proxy->prop1;// 'Hello'; $proxy->prop2='BAY'; echo MyClass::$prop2;// 'BAY'; echo isset($proxy->prop2);// true; echo isset($proxy->no_prop);// false; $proxy->prop2='test';// Errror:Cannot set new static class property unset($proxy->prop2);// Errror:Cannot unset static class property $proxy->prop2();// Errror:By default, you cannot call the property. But you can set a handler on the call action and the properties will be called according to the handler logic. echo $proxy->method(1);// return 2 foreach($proxy as $key=>$value){ echo $key." && ".$value; // prop1 && Hello; // prop2 && BAY; }
创建处理器类
Alpa \ ProxyObject \ Proxy
类的构造函数可以接受实现 Alpa \ ProxyObject \ Handlers \ ActionsInterface
接口的对象或类作为处理器。
<?php use Alpa\Tools\ProxyObject\Handlers\ActionsInterface; use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; class MyHandlersClass implements ActionsInterface { public function & run(string $action, $target,?string $prop,$value_or_arguments,ProxyInterface $proxy) { } public static function & static_run(string $action, $target,?string $prop,$value_or_arguments,ProxyInterface $proxy) { } } $target=(object)[]; $proxy = new Proxy ($target,MyHandlersClass::class); $handlers=new MyHandlersClass (); $proxy = new Proxy ($target,$handlers);
对于每个动作(set | get | isset | unset | call | invoke | toString | iterator),您需要实现相应的代码。
如果由于某些原因出现错误或其他问题,建议实现自己的处理器类以解决这个问题。
其他代理类
(v>=1.0.11) 在通过处理器创建代理对象时,可以指定要使用的代理类。
<?php use \Alpa\Tools\ProxyObject\Handlers; final class OtherProxy extends \Alpa\Tools\ProxyObject\ProxyAbstract { public function __construct($target,$handlers) { //Warning: the types must match the property types in the description. $this->target = $target; $this->handlers = $handlers; } } final class MyHandlersClass extends Instance { } $target=(object)[]; $proxy = new OtherProxy($target,MyHandlersClass::class); var_dump($proxy instanceof OtherProxy); //or $handlers=new MyHandlersClass(); $proxy = new OtherProxy($target,$handlers); var_dump($proxy instanceof OtherProxy); // or final class MyHandlersClass2 extends Instance { } ?> Thus, you can implement your own Proxy component by extending the behavior of the main one, but your component will not depend on the main component.
困难之处
对于代理对象的成员,应用如 property_exists
或 method_exists
或类似检查没有意义,因为它们将直接应用于代理对象。因此,在处理代理时,始终使用 isset
检查。如果您有需要同时检查属性和方法的复杂逻辑,建议分别实现处理属性和方法的逻辑。
<?php use Alpa\Tools\ProxyObject\Proxy; use Alpa\Tools\ProxyObject\ProxyInterface; use Alpa\Tools\ProxyObject\Hanclers\Instance; class MyHandlers extends Instance { protected bool $is_methods=false; public function __construct(bool $is_methods=false){ $this->is_methods=$is_methods; } protected function isset ( $target,string $prop,$val,ProxyInterface $proxy):bool { if($this->is_methods){ return method_exists($target,$prop); } return property_exists($target,$prop); } } class TargetClass { public $property='hello'; public function method(){} } $inst=new TargetClass(); $proxyProps=new Proxy($inst,new MyHandlers()); $proxyMethods=new Proxy($inst,new MyHandlers(true));