alpa/tools_proxy_object

创建一个代理对象,并在访问对象成员时分配获取/设置处理程序。

v1.0.14 2023-11-04 16:48 UTC

This package is auto-updated.

Last update: 2024-09-04 18:28:32 UTC


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\StaticActionsAlpa\ProxyObject\Handlers\InstanceActionsAlpa\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_existsmethod_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));