is there any possibility to "invoke" a class instance by a string representation?
In this case i would expect code to look like this:
class MyClass {
public $attribute;
}
$obj = getInstanceOf( "MyClass"); //$obj is now an instance of MyClass
$obj->attribute = "Hello World";
I think this must be possible, as PHP's SoapClient accepts a list of classMappings which is used to map a WSDL element to a PHP Class. But how is the SoapClient "invoking" the class instances?
$class = 'MyClass';
$instance = new $class;
However, if your class' constructor accepts a variable number of arguments, and you hold those arguments in an array (sort of call_user_func_array), you have to use reflection:
$class = new ReflectionClass('MyClass');
$args = array('foo', 'bar');
$instance = $class->newInstanceArgs($args);
There is also ReflectionClass::newInstance, but it does the same thing as the first option above.
Reference:
Object instantiation
ReflectionClass::newInstanceArgs()
ReflectionClass::newInstance()
The other answers will work in PHP <= 5.5, but this task gets a lot easier in PHP 5.6 where you don't even have to use reflection. Just do:
<?php
class MyClass
{
public function __construct($var1, $var2)
{}
}
$class = "MyClass";
$args = ['someValue', 'someOtherValue'];
// Here's the magic
$instance = new $class(...$args);
If the number of arguments needed by the constructor is known and constant, you can (as others have suggested) do this:
$className = 'MyClass';
$obj = new $className($arg1, $arg2, etc.);
$obj->attribute = "Hello World";
As an alternative you could use Reflection. This also means you can provide an array of constructor arguments if you don't know how many you will need.
<?php
$rf = new ReflectionClass('MyClass');
$obj = $rf->newInstanceArgs($arrayOfArguments);
$obj->attribute = "Hello World";
Related
I am trying to call a class' constructor through a callable, so I had the following code:
$callable = array('Foo', '__construct');
However calling this will throw the following error:
Fatal error: Non-static method Foo::__construct() cannot be called statically
I understand that the constructor is not a static method, but I can't use an existing instance to call the constructor for a new instance (as it will just call the constructor on the existing object again), is there any way at all to call a constructor like this?
If you're looking for a simple way to dynamically choose which class to construct, you can use a variable name with the new keyword, like so:
$inst = new $class_name;
// or, if the constructor takes arguments, provide those in the normal way:
$inst = new $class_name('foo', 'bar');
However, if what you need is a way of passing the constructor to something which is already expecting a callable, the best I can think of is to wrap it in an anonymous function:
$callable = function() { return new Foo; }
call_user_func( $callable );
Or using the short single-expression closure syntax introduced in PHP 7.4:
$callable = fn() => new Foo;
call_user_func( $callable );
If you really have to use call_user_func, this might work, though it's not clear why you would want to do this:
$reflection = new ReflectionClass("Foo");
$instance = $reflection->newInstanceWithoutConstructor();
call_user_func(array($instance, '__construct'));
The more correct answer, as #MauganRa already mentioned in a comment, is using \ReflectionClass::newInstance() or \ReflectionClass::newInstanceArgs(). I think this should be expressed in a full answer.
Now if you want to pass this around as a callback and use call_user_func() or call_user_func_array(), try this:
$callable = [new \ReflectionClass('C'), 'newInstance'];
Or a more fleshed-out example:
class Car {
private $color;
private $size;
public function __construct($color, $size) {
$this->color = $color;
$this->size = $size;
}
public function describe() {
return "A $this->size $this->color car.";
}
}
$callable = [new \ReflectionClass('Car'), 'newInstance'];
$cars = [];
$cars[] = call_user_func($callable, 'red', 'big')->describe();
$cars[] = call_user_func_array($callable, ['blue', 'small'])->describe();
var_export($cars);
Demo: https://3v4l.org/7fvQL
I am not aware of another / better solution.
Is there a way to set or modify the variable $this in PHP? In my case, I need to call an anonymous function where $this refers to a class that is not necessarily the class that made the call.
Pseudo-example:
function test() {
echo $this->name;
}
$user = new stdclass;
$user->name = "John Doe";
call_user_func(array($user, "test"));
Note: this will generate an error, because, in fact, the function expects an array containing an object and a method that exists in this object, and not any method of global scope.
Why not try setting the function definition to accept an object as a parameter? For example:
function test($object) {
if (isset($object->name)) // Make sure that the name property you want to reference exists for the class definition of the object you're passing in.
echo $object->name;
}
}
$user = new stdclass;
$user->name = "John Doe";
test($user); // Simply pass the object into the function.
The variable $this, when used in a class definition, refers to the object instance of the class. Outside of a class definition (or in a static method definition), variable $this has no special meaning. When you attempt to use $this outside of the OOP pattern, it loses meaning and call_user_func(), which relies on the OOP pattern, will not work in the way that you've attempted.
If you're using functions in a non-OOP way (like global functions), the function is not tied to any class/object and should be written in a non-OOP way (passing in data or otherwise using globals).
You can use the bind method on a closure object to change the meaning of this in a particular context. Note this functionality became available in PHP 5.4.
Official Description
Duplicates a closure with a specific bound object and class scope
class TestClass {
protected $var1 = "World";
}
$a = new TestClass();
$func = function($a){ echo $a." ".$this->var1; };
$boundFunction = Closure::bind($func, $a, 'TestClass');
$boundFunction("Hello");
// outputs Hello World
An alternative to this syntax it to use the bindTo method of an instance of a closure (anonymous function)
class TestClass {
protected $var1 = "World";
}
$a = new TestClass();
$func = function($a){ echo $a." ".$this->var1; };
$boundFunction = $func->bindTo($a, $a);
$boundFunction("Hello");
// outputs Hello World
In your example the relevant code would be
$test = function() {
echo $this->name;
};
$user = new stdclass;
$user->name = "John Doe";
$bound = $test->bindTo($user, $user);
call_user_func($bound);
how to get a class constructor function name without instantiating the class?
example:
$class = 'someClass';
$constructor = somehow get constructor;
$args = array();
$object = call_user_func_array(array($class,$constructor),$args);
what I need is to create a object by passing a undetermined number of variables into it's constructor.
You can do this with Reflection:
<?php
class Pants
{
public function __construct($a, $b, $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
}
}
$className = 'pants';
$class = new ReflectionClass($className);
$obj = $class->newInstanceArgs(array(1, 2, 3));
var_dump($obj);
This will also work if your constructor uses the old style (unless your code makes use of namespaces and you are using PHP 5.3.3 or, presumably, greater, as old-style constructors will no longer work with namespaced code - more info):
<?php
class Pants {
function Pants($a, $b, $c) { ... }
}
If the class has no constructor and you wish to use reflection, use $class->newInstance() instead of $class->newInstanceArgs(...). To do this dynamically, it would look like this:
$object = null === $class->getConstructor()
? $class->newInstance()
: $class->newInstanceArgs($args)
;
The function name of the constructor in PHP is always __construct, so you have it already without having to do anything.
I have this fetch function:
public static function fetch($class, $key)
{
try
{
$obj = new $class($key);
}
catch(Exception $e)
{
return false;
}
return $obj;
}
It creates a new instance by calling that class's constructor and passing in the key. Now, how would I make it so I can pass in an array of arguments in $key, and have it like:
$obj = new $class($key[0], $key[1]...);
So that it works for one or more keys?
Hopefully that was clear enough.
Using PHP 5
This is an interesting question. If it wasn't a constructor function you were trying to give dynamic arguments to, then normally you could use call_user_func_array(). However, since the new operator is involved, there doesn't seem to be an elegant way to do this.
Reflection seems to be the consensus from what I could find. The following snippet is taken from the user comments on call_user_func_array(), and illustrates the usage quite nicely:
<?php
// arguments you wish to pass to constructor of new object
$args = array('a', 'b');
// class name of new object
$className = 'myCommand';
// make a reflection object
$reflectionObj = new ReflectionClass($className);
// use Reflection to create a new instance, using the $args
$command = $reflectionObj->newInstanceArgs($args);
// this is the same as: new myCommand('a', 'b');
?>
To shorten it up for your case, you can use:
$reflectionObject = new ReflectionClass($class);
$obj = $reflectionObject->newInstanceArgs($key);
Use reflection:
$classReflection = new ReflectionClass($class);
$obj = $classReflection->newInstanceArgs($key);
My library solves this this:
// Returns a new instance of a `$classNameOrObj`.
function fuNew($classNameOrObj, $constructionParams = array()) {
$class = new ReflectionClass($classNameOrObj);
if (empty($constructionParams)) { return $class->newInstance(); }
return $class->newInstanceArgs($constructionParams); }
The empty() test is required because newInstanceArgs() will complain if you give it an empty array, stupidly.
What does the constructor of the class look like? Does it accept an arbitrary number of arguments? It might be better to accept an array of keys instead of a list of key arguments.
call_user_func_array could probably do what you want:
$obj = new $object_class();
call_user_func_array(array($obj, '__construct'), $args);
Note that this calls the constructor twice, which could have negative side effects.
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"));