Get class constructor - php

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.

Related

PHP 8: is it possible to change a class outside the class? [duplicate]

How can I create a property from a given argument inside a object's method?
class Foo{
public function createProperty($var_name, $val){
// here how can I create a property named "$var_name"
// that takes $val as value?
}
}
And I want to be able to access the property like:
$object = new Foo();
$object->createProperty('hello', 'Hiiiiiiiiiiiiiiii');
echo $object->hello;
Also is it possible that I could make the property public/protected/private ? I know that in this case it should be public, but I may want to add some magik methods to get protected properties and stuff :)
I think I found a solution:
protected $user_properties = array();
public function createProperty($var_name, $val){
$this->user_properties[$var_name] = $val;
}
public function __get($name){
if(isset($this->user_properties[$name])
return $this->user_properties[$name];
}
do you think it's a good idea?
There are two methods to doing it.
One, you can directly create property dynamically from outside the class:
class Foo{
}
$foo = new Foo();
$foo->hello = 'Something';
Or if you wish to create property through your createProperty method:
class Foo{
public function createProperty($name, $value){
$this->{$name} = $value;
}
}
$foo = new Foo();
$foo->createProperty('hello', 'something');
The following example is for those who do not want to declare an entire class.
$test = (object) [];
$prop = 'hello';
$test->{$prop} = 'Hiiiiiiiiiiiiiiii';
echo $test->hello; // prints Hiiiiiiiiiiiiiiii
Property overloading is very slow. If you can, try to avoid it. Also important is to implement the other two magic methods:
__isset();
__unset();
If you don't want to find some common mistakes later on when using these object "attributes"
Here are some examples:
http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members
EDITED after Alex comment:
You can check yourself the differences in time between both solutions (change $REPEAT_PLEASE)
<?php
$REPEAT_PLEASE=500000;
class a {}
$time = time();
$a = new a();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
$a->data = 'bye'.$a->data;
}
echo '"NORMAL" TIME: '.(time()-$time)."\n";
class b
{
function __set($name,$value)
{
$this->d[$name] = $value;
}
function __get($name)
{
return $this->d[$name];
}
}
$time=time();
$a = new b();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
//echo $a->data;
$a->data = 'bye'.$a->data;
}
echo "TIME OVERLOADING: ".(time()-$time)."\n";
Use the syntax: $object->{$property}
where $property is a string variable and
$object can be this if it is inside the class or any instance object
Live example: http://sandbox.onlinephpfunctions.com/code/108f0ca2bef5cf4af8225d6a6ff11dfd0741757f
class Test{
public function createProperty($propertyName, $propertyValue){
$this->{$propertyName} = $propertyValue;
}
}
$test = new Test();
$test->createProperty('property1', '50');
echo $test->property1;
Result: 50

How can I invoke a ReflectionFunction wrapping a closure that utilizes $this?

This is easiest to explain with an example:
class Example {
private $x;
public $f;
public function __construct() {
$this->x = 10;
$this->f = function() {
return $this->x;
};
}
}
$ex = new Example();
$f = new ReflectionFunction($ex->f);
echo $f->invoke().PHP_EOL;
Running this results in an error:
PHP Fatal error: Uncaught Error: Using $this when not in object context
That's because I've used $this in the closure, so it's really more like a ReflectionMethod, but ReflectionMethod doesn't seem to want to take a closure as an argument, so I'm not really sure what I can do.
How can I invoke $ex->f using reflection?
Well, I really don't know why that behavior is happening. But there's a workaround (well, I found it after a few tests).
As PHP doesn't let you explicit the bind of $this (it's bound automatically), you have to use an alternative variable:
$t = $this;
$this->f = function() use ($t) {
return $t->x;
};
The entire code:
class Example {
private $x;
public $f;
public function __construct() {
$this->x = 10;
$t = $this;
$this->f = function() use ($t) {
return $t->x;
};
}
}
$ex = new Example();
$f = new ReflectionFunction($ex->f);
echo $f->invoke().PHP_EOL;
And the result wanted
10
Tested on PHP 5.4, 5.5, 5.6 and 7.
UPDATE
After #mpen answer, I've realized about his restrictions and the use of Reflection.
When you use a ReflectionFunction to invoke a function, which is a closure at least, you should treat that as a closure. ReflectionFunction has a method called ReflectionFunction::getClosure().
The Class remains as #mpen created and the use will be as:
$ex = new Example();
$f = new ReflectionFunction($ex->f);
$closure = $f->getClosure();
echo $closure().PHP_EOL;
But only works on PHP 7.
For PHP 5.4, 5.5 and 5.6 you'll have to bind the class and scope. Weird, but it's the only way that I found using Closure::bindTo() or Closure::bind():
$ex = new Example();
$f = new ReflectionFunction($ex->f);
$closure = $f->getClosure();
$class = $f->getClosureThis();
$closure = $closure->bindTo($class , $class);
echo $closure().PHP_EOL;
Or just:
$ex = new Example();
$f = new ReflectionFunction($ex->f);
$class = $f->getClosureThis();
$closure = Closure::bind($f->getClosure() , $class , $class);
echo $closure().PHP_EOL;
It's very important to pass the class as scope (second parameter) which will determine whether you can access private/protected variable or not.
The second paramater also could be the class name as:
$closure = $closure->bindTo($class , 'Example');//PHP >= 5.4
$closure = $closure->bindTo($class , get_class($class));//PHP >= 5.4
$closure = $closure->bindTo($class , Example::class);//PHP 5.5
But I didn't concerned about performance, so the class passed twice is just fine to me.
There's also the method Closure::call() which can be used to change the scope, but also just for PHP >= 7.

PHP: Creating class properties from within function [duplicate]

How can I create a property from a given argument inside a object's method?
class Foo{
public function createProperty($var_name, $val){
// here how can I create a property named "$var_name"
// that takes $val as value?
}
}
And I want to be able to access the property like:
$object = new Foo();
$object->createProperty('hello', 'Hiiiiiiiiiiiiiiii');
echo $object->hello;
Also is it possible that I could make the property public/protected/private ? I know that in this case it should be public, but I may want to add some magik methods to get protected properties and stuff :)
I think I found a solution:
protected $user_properties = array();
public function createProperty($var_name, $val){
$this->user_properties[$var_name] = $val;
}
public function __get($name){
if(isset($this->user_properties[$name])
return $this->user_properties[$name];
}
do you think it's a good idea?
There are two methods to doing it.
One, you can directly create property dynamically from outside the class:
class Foo{
}
$foo = new Foo();
$foo->hello = 'Something';
Or if you wish to create property through your createProperty method:
class Foo{
public function createProperty($name, $value){
$this->{$name} = $value;
}
}
$foo = new Foo();
$foo->createProperty('hello', 'something');
The following example is for those who do not want to declare an entire class.
$test = (object) [];
$prop = 'hello';
$test->{$prop} = 'Hiiiiiiiiiiiiiiii';
echo $test->hello; // prints Hiiiiiiiiiiiiiiii
Property overloading is very slow. If you can, try to avoid it. Also important is to implement the other two magic methods:
__isset();
__unset();
If you don't want to find some common mistakes later on when using these object "attributes"
Here are some examples:
http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members
EDITED after Alex comment:
You can check yourself the differences in time between both solutions (change $REPEAT_PLEASE)
<?php
$REPEAT_PLEASE=500000;
class a {}
$time = time();
$a = new a();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
$a->data = 'bye'.$a->data;
}
echo '"NORMAL" TIME: '.(time()-$time)."\n";
class b
{
function __set($name,$value)
{
$this->d[$name] = $value;
}
function __get($name)
{
return $this->d[$name];
}
}
$time=time();
$a = new b();
for($i=0;$i<$REPEAT_PLEASE;$i++)
{
$a->data = 'hi';
//echo $a->data;
$a->data = 'bye'.$a->data;
}
echo "TIME OVERLOADING: ".(time()-$time)."\n";
Use the syntax: $object->{$property}
where $property is a string variable and
$object can be this if it is inside the class or any instance object
Live example: http://sandbox.onlinephpfunctions.com/code/108f0ca2bef5cf4af8225d6a6ff11dfd0741757f
class Test{
public function createProperty($propertyName, $propertyValue){
$this->{$propertyName} = $propertyValue;
}
}
$test = new Test();
$test->createProperty('property1', '50');
echo $test->property1;
Result: 50

How to "invoke" a class instance in PHP?

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";

instantiate a class from a variable in PHP?

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"));

Categories