2

我编写了一个相当简单的延迟加载代理类,我过去在http://blog.simonholywell.com/post/2072272471/logging-global-php-objects-lazy-loading-proxy上记录了它

现在,当我将另一个项目转换为使用它时,我被代理一个方法绊倒了,该方法的一个参数通过引用传递给它。当这通过我的代理类的 __call 方法时,我得到:

致命错误:方法 LazyLoader::__call() 无法通过 /home/simon/file/name.php 中的引用获取参数

关于如何解决或解决这个问题的任何聪明的想法。如果可能的话,最好不要重构需要通过引用传递的代码。

延迟加载代理类看起来像这样,但我的博文中的描述更好地解释了目的:

<?php
/**
 * @author Simon Holywell <treffynnon@php.net>
 */
class LazyLoadingProxy {
    /**
     * Where the instance of the actual class is stored.
     * @var $instance object
     */
    private $instance = null;

    /**
     * The name of the class to load
     * @var $class_name string
     */
    private $class_name = null;

    /**
     * The path to the class to load
     * @var $class_path string
     */
    private $class_path = null;

    /**
     * Set the name of the class this LazyLoader should proxy
     * at the time of instantiation
     * @param $class_name string
     */
    public function __construct($class_name, $class_path = null) {
        $this->setClassName($class_name);
        $this->setClassPath($class_path);
    }

    public function setClassName($class_name) {
        if(null !== $class_name) {
            $this->class_name = $class_name;
        }
    }

    public function getClassName() {
        return $this->class_name;
    }

    public function setClassPath($class_path) {
        if(null !== $class_path) {
            $this->class_path = $class_path;
        }
    }

    public function getClassPath() {
        return $this->class_path;
    }

    /**
     * Get the instance of the class this LazyLoader is proxying.
     * If the instance does not already exist then it is initialised.
     * @return object An instance of the class this LazyLoader is proxying
     */
    public function getInstance() {
        if(null === $this->instance) {
            $this->instance = $this->initInstance();
        }
        return $this->instance;
    }

    /**
     * Load an instance of the class that is being proxied.
     * @return object An instance of the class this LazyLoader is proxying
     */
    private function initInstance() {
        Logger::log('Loaded: ' . $class_name);
        require_once($this->class_path);
        $class_name = $this->class_name;
        return new $class_name();
    }

    /**
     * Magic Method to call functions on the class that is being proxied.
     * @return mixed Whatever the requested method would normally return
     */
    public function __call($name, &$arguments) {
        $instance = $this->getInstance();
        Logger::log('Called: ' . $this->class_name . '->' . $name . '(' . print_r($arguments, true) . ');');
        return call_user_func_array(
                array($instance, $name),
                $arguments
            );
    }

    /**
     * These are the standard PHP Magic Methods to access
     * the class properties of the class that is being proxied.
     */
    public function __get($name) {
        Logger::log('Getting property: ' . $this->class_name . '->' . $name);
        return $this->getInstance()->$name;
    }

    public function __set($name, $value) {
        Logger::log('Setting property: ' . $this->class_name . '->' . $name);
        $this->getInstance()->$name = $value;
    }

    public function __isset($name) {
        Logger::log('Checking isset for property: ' . $this->class_name . '->' . $name);
        return isset($this->getInstance()->$name);
    }

    public function __unset($name) {
        Logger::log('Unsetting property: ' . $this->class_name . '->' . $name);
        unset($this->getInstance()->$name);
    }
}

非常感谢任何帮助。

4

3 回答 3

4

简短的回答是不要通过引用传递。在 99.9% 的情况下,您不需要它。在其他 0.1% 中,您无论如何都可以解决缺乏参考的问题。请记住,无论如何,对象都是通过对象引用传递的,因此您不需要为它们使用变量引用。

现在,就解决方法而言,我会亲自将其硬编码到适配器中。因此,为该特定类扩展您的代理,并为该特定方法包含一个包装器。然后实例化那个新的扩展类而不是那个类的核心代理。脏吗?绝对地。但这是您唯一的解决方法,无需重构原始类以不通过引用获取参数,或重构调用者以防止通过引用传递(无论如何都已弃用)。

于 2011-02-18T16:11:29.933 回答
1

这是一个技巧,通过 __call 魔术方法通过引用传递变量:我现在不知道这将工作多长时间或在巫婆 php 版本中。我在 php 5.3.2 上测试过

Fitst 你不能在 __call 函数定义中通过 $reference 传递 $arguments 变量。因为 php 出错了。

所以首先:这是一个不做的代码,你想要的引用参数,但几乎很好;)

    class A {  public $x = array();  }

    class Teszt{
        private function addElement( &$list ){
            $list->x[] = 'new element';
            return count($list->x);
        }

        public function __call($name,$arguments){
            if (!preg_match('#^add([0-9]+)Element$#', $name, $matches) || $matches[1]<1){
               trigger_error ("Function is not exists teszt::$name", E_USER_ERROR);
            }                
            $ret = null;
            for($i=0;$i<$matches[1];$i++){
                $ret = call_user_func_array(array($this,'addElement'), $arguments);
            }
            return $ret;
        }
   }

    $a = new A();
    $a->x = array('old element','old element');
    $t = new Teszt();
    $cnt = $t->add5Element($a);
    var_dump($a);
    var_dump($cnt);

输出:

    PHP Warning:  Parameter 1 to Teszt::addElement() expected to be a reference, value given in /home/gkovacs/callMagicWithReference.php on line 19
    PHP Stack trace:
    PHP   1. {main}() /home/gkovacs/callMagicWithReference.php:0
    PHP   2. Teszt->add2Element() /home/gkovacs/callMagicWithReference.php:27
    PHP   3. Teszt->__call() /home/gkovacs/callMagicWithReference.php:0
    PHP   4. call_user_func_array() /home/gkovacs/callMagicWithReference.php:19
    PHP Warning:  Parameter 1 to Teszt::addElement() expected to be a reference, value given in /home/gkovacs/callMagicWithReference.php on line 19
    PHP Stack trace:
    PHP   1. {main}() /home/gkovacs/callMagicWithReference.php:0
    PHP   2. Teszt->add2Element() /home/gkovacs/callMagicWithReference.php:27
    PHP   3. Teszt->__call() /home/gkovacs/callMagicWithReference.php:0
    PHP   4. call_user_func_array() /home/gkovacs/callMagicWithReference.php:19
    object(A)#1 (1) {
      ["x"]=>
      array(2) {
        [0]=>
        string(11) "old element"
        [1]=>
        string(11) "old element"
      }
    }
    NULL

ohhh :((( 在“HACK”之后:

    class A {  public $x = array();  }

    class Teszt{
        private function addElement( &$list ){
            $list->x[] = 'new element';
            return count($list->x);
        }

        public function __call($name,$arguments){
            if (!preg_match('#^add([0-9]+)Element$#', $name, $matches) || $matches[1]<1){
               trigger_error ("Function is not exists teszt::$name", E_USER_ERROR);
            }                
            $ret = null;
            //Look at my hand, because i will cheat
            foreach($arguments as $k=>&$v){ } 
            //end of cheat 
            for($i=0;$i<$matches[1];$i++){
                $ret = call_user_func_array(array($this,'addElement'), $arguments);
            }

            return $ret;
        }
   }

    $a = new A();
    $a->x = array('old element','old element');
    $t = new Teszt();
    $cnt = $t->add5Element($a);
    var_dump($a);
    var_dump($cnt);

输出:

object(A)#1 (1) {
  ["x"]=>
  array(4) {
    [0]=>
    string(11) "old element"
    [1]=>
    string(11) "old element"
    [2]=>
    string(11) "new element"
    [3]=>
    string(11) "new element"
  }
}
int(4)

如我们所愿。这仅适用于对象,但不适用于数组。有一种使用 array-s 来调用时间传递引用的方法......像这样:(当然你也可以对对象使用这种方式)

    $cnt = $t->add5Element(&$a);

在这种情况下 php 生成通知...

如果可能的话,重新定义函数的最简单方法。在我的代码中: private functionaddElement($list) ,不要在参数列表中定义为引用。正如您在上一条消息中所读到的,它会起作用,因为对象是通过引用自动传递的。但有时,由于实现的接口或其他原因,您无法重新定义函数...

于 2013-01-28T17:46:00.327 回答
1

它可以。饿了也一样。

    <?php
        class MyClass(){
          public function __call($name, $params)
          {
            var_dump($params);
            $params[0][0] = '222';
          }
        }
        $obj = new MyClass();    
        $info = 3;

        var_dump($info);
        $obj->setter([&$info]);
        var_dump($info);
于 2014-03-29T17:28:45.097 回答