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).
Related
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!')
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
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();
I would like to assign a static function to a variable so that I can send it around as a parameter. For example:
class Foo{
private static function privateStaticFunction($arg1,$arg2){
//compute stuff on the args
}
public static function publicStaticFunction($foo,$bar){
//works
$var = function(){
//do stuff
};
//also works
$var = function($someArg,$someArg2){
//do stuff
};
//Fatal error: Undefined class constant 'privateStaticFunction'
$var = self::privateStaticMethod;
//same error
$var = Foo::privateStaticFunction;
//compiles, but errors when I try to run $var() somewhere else, as expected
//Fatal error: Call to private method Foo::privateStaticMethod() from context ''
$var = function(){
return Foo::privateStaticMethod();
};
}
}
I've tried a few more variations but none of them worked.
I don't even expect this sort of functional hacking to work with PHP but hey, who knows?
Is it possible to do that in PHP or will I need to come up with some hack using eval?
P.S.: LawnGnome on ##php mentioned something about it being possible to do what I want using array('Foo','privateStaticMethod') but I didn't understand what he meant and I didn't press him further as he looked busy.
You want to use call_user_func with an array argument.
$var = array('Foo','privateStaticMethod');
call_user_func($var);
Note that the "private" specifier on that method will make it only callable this way within the Foo class.
call_user_func takes a callable as the first argument. Note that as of PHP 5.2.3, it is possible to avoid the array notation in your case.
Static class methods can also be passed without instantiating an
object of that class by passing the class name instead of an object at
index 0. As of PHP 5.2.3, it is also possible to pass
'ClassName::methodName'.
As a note, you should set your static variable as so:
static $privateStaticMethod;
$var = self::$privateStaticMethod;
Leaving off a $ when using self:: will try to access a class CONSTANT and not a variable.
In actuality, I think you meant this line to be:
//Fatal error: Undefined class constant 'privateStaticFunction'
$var = self::privateStaticFunction("arga", "argb");
If you are on PHP 5.4 you can do the following within your publicStaticFunction:
$var = function(){
return self::privateStaticFunction();
};
$var();
Within PHP 5.4, PHP allows you to access class scope from lambda functions.
Also, have you looked into ReflectionClass?
The following would be used to replace call_user_func_array() in a more OOP style:
<?php
$rc = new ReflectionClass('SomeClass');
$class = $rc->newInstanceArgs(array('foo', 'bar'));
echo $class->doSomething();
You could write your own class to use ReflectionMethod which implements ReflectionClass and use setAccessible() to allow access to protected and private methods.
Here is what I did to solve my issue
class test{
static function Unix($arr){return implode("/",$arr);}
static function Windows($arr){return implode("\\",$arr);}
function test(){
$slash = "Unix";
echo self::$slash(["path","to","a","folder"]);
}
}
I know this question sounds rather vague so I will make it more clear with an example:
$var = 'bar';
$bar = new {$var}Class('var for __construct()'); //$bar = new barClass('var for __construct()');
This is what I want to do. How would you do it? I could off course use eval() like this:
$var = 'bar';
eval('$bar = new '.$var.'Class(\'var for __construct()\');');
But I'd rather stay away from eval(). Is there any way to do this without eval()?
Put the classname into a variable first:
$classname=$var.'Class';
$bar=new $classname("xyz");
This is often the sort of thing you'll see wrapped up in a Factory pattern.
See Namespaces and dynamic language features for further details.
If You Use Namespaces
In my own findings, I think it's good to mention that you (as far as I can tell) must declare the full namespace path of a class.
MyClass.php
namespace com\company\lib;
class MyClass {
}
index.php
namespace com\company\lib;
//Works fine
$i = new MyClass();
$cname = 'MyClass';
//Errors
//$i = new $cname;
//Works fine
$cname = "com\\company\\lib\\".$cname;
$i = new $cname;
How to pass dynamic constructor parameters too
If you want to pass dynamic constructor parameters to the class, you can use this code:
$reflectionClass = new ReflectionClass($className);
$module = $reflectionClass->newInstanceArgs($arrayOfConstructorParameters);
More information on dynamic classes and parameters
PHP >= 5.6
As of PHP 5.6 you can simplify this even more by using Argument Unpacking:
// The "..." is part of the language and indicates an argument array to unpack.
$module = new $className(...$arrayOfConstructorParameters);
Thanks to DisgruntledGoat for pointing that out.
class Test {
public function yo() {
return 'yoes';
}
}
$var = 'Test';
$obj = new $var();
echo $obj->yo(); //yoes
I would recommend the call_user_func() or call_user_func_arrayphp methods.
You can check them out here (call_user_func_array , call_user_func).
example
class Foo {
static public function test() {
print "Hello world!\n";
}
}
call_user_func('Foo::test');//FOO is the class, test is the method both separated by ::
//or
call_user_func(array('Foo', 'test'));//alternatively you can pass the class and method as an array
If you have arguments you are passing to the method , then use the call_user_func_array() function.
example.
class foo {
function bar($arg, $arg2) {
echo __METHOD__, " got $arg and $arg2\n";
}
}
// Call the $foo->bar() method with 2 arguments
call_user_func_array(array("foo", "bar"), array("three", "four"));
//or
//FOO is the class, bar is the method both separated by ::
call_user_func_array("foo::bar"), array("three", "four"));