0

Need dynamically append method to class.

My code:

<?php

class stdClass1 {
    public function __call($method, $arguments) {
        return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
    }
}

class stdClass2 {

    function stdRunMethod() {
        $obj = new stdClass1();
        $obj->test = function() {
           echo 'a simple function';
        };
        $obj->test();

        $obj2 = new stdClass1();
        $obj2->test();
    }

}

$obj = new stdClass2();
$obj->stdRunMethod();

Question: why test method run only for first instance of stdClass1 class? How to append this method for all new instances?

stdex
  • 359
  • 1
  • 6
  • 16
  • 1
    Because you only assign the function to the property of the first object. When you modify the object, you don't modify the class. – frz3993 Aug 22 '16 at 09:19
  • Yes. This way I can add methods to class instances. But I need dynamically add methods directly to the classes. Is it possible? – stdex Aug 22 '16 at 09:21
  • Perhaps using Reflection it is possible. Or maybe you can create a container to add the property after object creation. – frz3993 Aug 22 '16 at 09:26
  • Possible duplicate of [How to add a new method to a php object on the fly?](http://stackoverflow.com/questions/2938004/how-to-add-a-new-method-to-a-php-object-on-the-fly) – jannej Aug 22 '16 at 09:29

2 Answers2

2

try this instead (demo):

   <?php 

    class stdClass1 extends \stdClass
    {
        private static $addedClosures = array();

        public function __set($name, $value)
        {
            if ($value instanceof \Closure) {
                self::$addedClosures[$name] = $value;
            }
            else {
                parent::__set($name, $value);
            }
        }

        public function __call($method, $arguments)
        {
            if (isset(self::$addedClosures[$method]))
                return call_user_func_array(self::$addedClosures[$method], $arguments);
            return call_user_func_array($method, $arguments);
        }
    }

    class stdClass2 extends \stdClass
    {

        function stdRunMethod()
        {
            $obj = new stdClass1();
            $obj->test = function () {
                print_r('a simple function');
            };
            $obj->test();

            $obj2 = new stdClass1();
            $obj2->test();
        }

    }
Ahmad Hajjar
  • 1,796
  • 3
  • 18
  • 33
1

The reason it only runs once is that each copy of stdClass1 maintains their own set of variables. In the following

$obj1 = new stdClass1();
$obj1->a = '1';

$obj2 = new stdClass1();
$obj2->a = '2';

echo $obj1->a;

You'll get the value 1 as the output. Because in most cases they maintain different references. Unless you use the static keyword. Static properties are shared between all instances of the class, and should be used carefully, but that's what you're thinking of. What you're thinking of can be done like this

<?php

class stdClass1 {
    private static $methods = [];

    public function __call($method, $arguments) {
        return call_user_func_array(Closure::bind($this->methods[$method], $this, get_called_class()), $arguments);
    }

    public function __set($name, $value) {
        if (is_callable($value)) {
            $this->methods[$name] = $value;
        } else {
            parent::__set($name, $value);
        }
    }
}

Here we're using a static, defined property to hold all of the dynamic methods, and we're using the magic __set property to set the methods in to the array.

That being said, dynamically loading methods in to an object is bad. Don't do that

Gareth Parker
  • 5,012
  • 2
  • 18
  • 42