28

Is it possible to add a method/function in this way, like

$arr = array(
    "nid"=> 20,
    "title" => "Something",
    "value" => "Something else",
    "my_method" => function($arg){....}
);

or maybe like this

$node = (object) $arr;
$node->my_method=function($arg){...};

and if it's possible then how can I use that function/method?

Bohemian
  • 412,405
  • 93
  • 575
  • 722
The Alpha
  • 143,660
  • 29
  • 287
  • 307

5 Answers5

36

This is now possible to achieve in PHP 7.1 with anonymous classes

$node = new class {
    public $property;

    public function myMethod($arg) { 
        ...
    }
};

// and access them,
$node->property;
$node->myMethod('arg');
  • 1
    Nice one, I was mocking some data and this proved pretty helpful. – Nick M Sep 28 '17 at 00:08
  • 3
    but does this add a new function to an already defined object? – Geomorillo Oct 11 '18 at 02:53
  • @Geomorillo, No, it does not add to object, you can "add function to class" with `extends` on (anonymous) class. It is php, it is not JavaScript. But on other answers you can see also unreadable solutions with `__call`. – user1742529 Feb 23 '22 at 02:23
20

You cannot dynamically add a method to the stdClass and execute it in the normal fashion. However, there are a few things you can do.

In your first example, you're creating a closure. You can execute that closure by issuing the command:

$arr['my_method']('Argument')

You can create a stdClass object and assign a closure to one of its properties, but due to a syntax conflict, you cannot directly execute it. Instead, you would have to do something like:

$node = new stdClass();
$node->method = function($arg) { ... }
$func = $node->method;
$func('Argument');

Attempting

$node->method('Argument')

would generate an error, because no method "method" exists on a stdClass.

See this SO answer for some slick hackery using the magic method __call.

Community
  • 1
  • 1
keelerm
  • 2,873
  • 20
  • 12
11

Since PHP 7 it is also possible to directly invoke an anonymous function property:

$obj = new stdClass;
$obj->printMessage = function($message) { echo $message . "\n"; };
echo ($obj->printMessage)('Hello World'); // Hello World

Here the expression $obj->printMessage results in the anonymous function which is then directly executed with the argument 'Hello World'. It is however necessary to put the function expression in paranetheses before invoking it so the following will still fail:

echo $obj->printMessage('Hello World'); 
// Fatal error: Uncaught Error: Call to undefined method stdClass::printMessage()
goulashsoup
  • 2,639
  • 2
  • 34
  • 60
0

Another solution would be to create an anonymous class and proxy the call via the magic function __call, with arrow functions you can even keep reference to context variables:

 new Class ((new ReflectionClass("MyClass"))->getProperty("myProperty")) {
            public function __construct(ReflectionProperty $ref)
            {
                $this->setAccessible = fn($o) => $ref->setAccessible($o);
                $this->isInitialized = fn($o) => $ref->isInitialized($o);
                $this->getValue = fn($o) => $ref->getValue($o);
            }

            public function __call($name, $arguments)
            {
                $fn = $this->$name;
                return $fn(...$arguments);
            }

    }
g13013
  • 305
  • 2
  • 11
0
class myclass {
function __call($method, $args) {
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
$obj = new myclass();
 $obj->method = function($var) { echo $var; };

$obj->method('a');

Or you can create defult class and use...

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
Abolfazl
  • 1
  • 2