Pass object function as callback - php

are there ways to pass a "method reference" to a function that accept a callback?
Eg.:
function ex($callback){
$callback();
}
$obj = (object) ['f' => function(){echo "hello";}];
ex($obj->a)
Obviously this is a very simplistic case, image having a complex object $obj with parameters inside

PHP does not work like Javascript.
This code:
$obj = (object) ['f' => function(){echo "hello";}];
Does not define "an object with method f()" it defines "an object with property f that happens to be a callable type".
Calling $obj->f(); directly results in:
Fatal error: Uncaught Error: Call to undefined method stdClass::f()
It would need to be specially handled like:
$func = $obj->f;
$func();
Or:
($obj->f)();
While not a wholly accurate statement, you could consider it that callable types need the equivalent of "being dereferenced" before they can actually be called.
You example is something of a "successful error" in that it attempts to use "first class functions" which PHP does not have, but on a malformed object that allows this to not fail.
Using a proper class:
function ex($callback){
$callback();
}
class Example {
public function f_instance() {
echo "instance call\n";
}
public static function f_static() {
echo "static call\n";
}
}
ex(['Example', 'f_static']);
$e = new Example();
ex([$e, 'f_instance']);
Output:
static call
instance call
Ref: https://www.php.net/manual/en/language.types.callable.php

Related

How to call a method of anonymous object php?

I need to create anonymous object and call it's method
$obj = new stdClass();
$obj->Greeting = function (string $d){return "Hello ".$d;};
$greetings = $obj->Greeting("world!");
But when I try to execute this code I get an error
Call to undefined method stdClass::Greeting()
What's wrong?
You created a stdClass object, not an anonymous one:
$obj = new class () {
public function Greeting(string $d)
{
return "Hello $d";
}
};
echo $greetings = $obj->Greeting("world!");
output:
Hello world!
What's wrong?
Nothing, let's just ask, what's behind or happening here?
The stdClass is used for "empty" objects in PHP or when casting an array to an object ($obj = (object) ['hello' => 'world']).
By default it has no properties (like in $obj = new stdClass;) and also no methods. It is empty in terms of both of these.
Properties can be added dynamically to an stdClass object - but not functions as class methods have to be declared in PHP before instantiating the object.
So the function in your case is a property (PHP has two bags here: one for properties and one for functions) and not a new method dynamically added to it (class MyClass { function method() {...} }).
Let's compare with the original example and provoke the error again:
$obj = new stdClass();
$obj->Greeting = function (string $d) {
return "Hello $d";
};
$greetings = $obj->Greeting("world!");
PHP Fatal error: Uncaught Error: Call to undefined method stdClass::Greeting()
However:
echo $greetings = ($obj->Greeting)("world!");
# #
works, the output:
Hello world!
because PHP is now guided to "call" the ($obj->Greeting) property indirectly, so not looking for the stdClass::Greeting method first.
Normally you don't want that indirection, therefore the suggestion to use the anonymous class instead.
Change
$obj->Greeting("world!");
to
($obj->Greeting)("world!");
or use call_user_func() :
call_user_func($obj->Greeting, 'world!')

Magic method to run when object is referenced by a variable in an echo statement

In PHP does such a magic method automatically run when a variable referencing an object is in an echo statement?
I am sorry, I had quite a difficulty understanding what you're asking. I believe you want the __toString() method:
The __toString() method allows a class to decide how it will react when it is treated like a string. For example, what echo $obj; will print. This method must return a string, as otherwise a fatal E_RECOVERABLE_ERROR level error is emitted.
Here's a quick example:
class A
{
public function __toString()
{
return 'banana';
}
}
$a = new A();
echo $a;
This will print out banana

instantiating a object on the fly a static method?

I'm not so sure what the following object instantiation is called but it comes from a article that i'm reading.
class foo
{
function out()
{
return 'hello';
}
}
echo (new foo())->out();
The object is instantiated automatically and calls the out method. But what i dont really understand is when i rename the out() method into a fictitious method i get an error like this:
example:
class foo
{
function out()
{
return 'hello';
}
}
echo (new foo())->ou();
Fatal error: Call to undefined method foo::ou() in ...
Is this method somehow being called as a static method?
The :: does not stand for static method, this is a missconception. The :: is a "scope resolution operator", it denotes the identification of a method by its class predicated full name.
So this simply means: "method 'ou' as defined by class 'foo'". Not more, not less.
No. The error just indicates that the method doesn't exist. It always shows the :: for this error, no matter whether you call the method in a static way or not. You would get the same error if you changed the code to:
$foo = new foo();
echo $foo->ou();
Second code example as per request in comments:
$moo = new moo(); // Parentheses optional, I guess
$foo = new foo($moo);
$foo->out();

Call class method from inside array_map anonymous function

I am trying to call one of my object's methods from within an array_map anonymous function. So far I am receiving the expected error of:
Fatal error: Using $this when not in object context in...
I know why I am getting this error, I just don't know a way to achieve what I am trying to... Does anybody have any suggestions?
Here is my current code:
// Loop through the data and ensure the numbers are formatted correctly
array_map(function($value){
return $this->some_method($value,'value',false);
},$this->mssql->data[0]['results'][0]);
You can tell the function to "close over" the $this variable by using the "use" keyword
$host = $this;
array_map(function($value) use ($host) {
return $host->some_method($value,'value',false);
},$this->mssql->data[0]['results'][0]);
Also, you can call your map function from a class context and you will not receive any errors. Like:
class A {
public $mssql = array(
'some output'
);
public function method()
{
array_map(function($value){
return $this->mapMethod($value,'value',false);
},$this->mssql);
}
public function mapMethod($value)
{
// your map callback here
echo $value;
}
}
$a = new A();
$a->method();

Declaring an anonymous function within new stdClass

Just wondering why something like this doesn't work:
public function address($name){
if(!isset($this->addresses[$name])){
$address = new stdClass();
$address->city = function($class = '', $style = ''){
return $class;
};
$this->addresses[$name] = $address;
}
return $this->addresses[$name];
}
Calling it like echo $class->address('name')->city('Class') should just echo Class, however I get Fatal error: Call to undefined method stdClass::city()
I can find a better way to do this, because this will get messy, but I'm wondering what I might be doing wrong there, or if PHP doesn't support this and why.
PHP is right when invoke fatal error Call to undefined method stdClass::city() because object $class->address('name') has no method city.
Intead, this object has property city which is instance of Closure Class (http://www.php.net/manual/en/class.closure.php)
You can verify this: var_dump($class->address('name')->city)
I found the way to call this anonymous function is:
$closure = $class->address('name')->city;
$closure('class');
Hope this helps!
Sadly it is not possible within stdClass, but there is a workaround -- PHP Anonymous Object.
// define by passing in constructor
$anonim_obj = new AnObj(array(
"foo" => function() { echo "foo"; },
"bar" => function($bar) { echo $bar; }
));
$anonim_obj->foo(); // prints "foo"
$anonim_obj->bar("hello, world"); // prints "hello, world"
AFAIK, this is not supported by PHP, and you must use the call_user_func() or call_user_func_array() functions to call closures assigned to class properties (usually you can use __call() to do this, but in your case, the class is stdClass, so this isn't possible).

Categories