Viewed   148 times

How could I call the constructor of a class with call_user_func_array

It is not possible to do :

$obj = new $class();
call_user_func_array(array($obj, '__construct'), $args); 

because if the constructor has parameters, the new will fail.

Constraint : I do not control the classes that I have to instantiate, nor can I modify them.

Don't ask me why I want to do this crazy thing, this is a crazy test.

 Answers

1

You can use reflection like:

$reflect  = new ReflectionClass($class);
$instance = $reflect->newInstanceArgs($args);

As of PHP 5.6.0, the ... operator can also be used for this purpose.

$instance = new $class(...$args);

if(version_compare(PHP_VERSION, '5.6.0', '>=')){
    $instance = new $class(...$args);
} else {
    $reflect  = new ReflectionClass($class);
    $instance = $reflect->newInstanceArgs($args);
}
Monday, August 8, 2022
1

I think for now the only way to do what you want is:

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b, $c);
    }
}

Edit 2:

My advice, based on over a year of dealing with traits in PHP, is: avoid writing constructors in traits at all, or if you must - at least make them parameterless. Having them in traits goes against the idea of constructors in general, which is: constructors should be specific to a class to which they belong. Other, evolved high-level languages don't even support implicit constructor inheritance. This is because constructors have far more stronger relation to the class then other methods. In fact they have so strong relation, that even the LSP does not apply to them. The traits in Scala language (a very mature and SOLID-friendly successor of Java), can't have a constructor with parameters.

Edit 1:

There was a bug in PHP 5.4.11, which actually allowed to alias a superclass method. But this was considered a no-no by the PHP developers, so we are still stuck with that cumbersome solution which I presented above. But that bug raised a discussion about what can be done with this, and I'm hoping it will be targeted in future releases.

Meanwhile I came across the same problem over and over again. My irritation raised exponentially with the number of parameters and lines of docblock which had to be repeated a lot of times in order to use the trait. So I came up with the following pattern in order to stick to the DRY rule as much as I could:

Instead of repeating entire set of parameters like this:

trait SayWorld {

    /**
     * This is a valid docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     */
    public function __construct($a, $b) {
        echo (int)$c * ($a+$b);
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * Repeated and unnecessary docblock.
     *
     * @param int $a Doc comment.
     * @param int $b Doc comment.
     * @param int $c Doc comment.
     */
    public function __construct($a, $b, $c = 0)
    {
        $this->__swConstruct($a, $b);
    }
}

I write a class much like a tuple (concept familiar to C# and Python users), and use it instead of an endless list of parameters:

class SayWorldConstructTuple
{
    public $a;

    public $b;

    public function __construct($a, $b)
    {
        $this->a = $a;
        $this->b = $b;
    }
}

class MyHelloWorld extends Base {

    use SayWorld {
        SayWorld::__construct as private __swConstruct;
    }

    /**
     * New and valid docblock.
     *
     * @param SayWorldConstructTuple $Tuple
     * @param int $c Additional parameter.
     */
    public function __construct(SayWorldConstructTuple $Tuple, $c = 0)
    {
        $this->__swConstruct($Tuple->a, $Tuple->b);
        $this->c = $c;
    }
}

Note: this pattern is of course more useful with a larger amount of tuple's constructor parameters, and more classes using the tuple.

It can be automated further with the use of PHP's dynamic nature.

Saturday, December 10, 2022
2

Java classes have default, no args constructor.

Does anyone know why PHP creators made it this way?

Java was a planned language. It was rigorously considered and formally specified from the get-go. It was intended to last as long as possible with minimal changes for sake of backwards and forwards compatibility, for simplicity to developers and implementers alike. The definitely didn't succeed to the degree they hoped, but you can still take Java 1.0 code, compile it and run it on modern VMs.

PHP's designers never planned the language to nearly such an extreme. They make it up as they go along. For better or worse, they're moderately willing to throw the language apart and rebuild it in the next version. It's changed unrecognizably since PHP 1 and 2. Even recently, in PHP 5, you had the dramatic change from by-value to by-reference semantics for objects. It's that same brilliant methodology that brought us magic quotes, the lack of Unicode, inconsistently named (and frequently renamed) functions, and a parser that without even a hint of error (even with error_reporting(-1);), will merrily interpret the numeric literal 09 as 0.

PHP behavior can cause troubles when we remove no args constructor from parent class, eg. when we think it's not needed any more.

You could request this be changed on bugs.php.net, but chances are they'll ignore it or fob you off with "Sorry, but your issue does not imply a problem in PHP itself...".

Monday, September 5, 2022
4

Read all of Constructors and Destructors.

Constructors can take parameters like any other function or method in PHP:

class MyClass {

  public $param;

  public function __construct($param) {
    $this->param = $param;
  }
}

$myClass = new MyClass('foobar');
echo $myClass->param; // foobar

Your example of how you use constructors now won't even compile as you can't reassign $this.

Also, you don't need the curly brackets every time you access or set a property. $object->property works just fine. You only need to use curly-brackets under special circumstances like if you need to evaluate a method $object->{$foo->bar()} = 'test';

Tuesday, August 16, 2022
 
baski
 
1

I believe what you are looking for is just calling the init function again.

class SomeClass(object):
    def __init__(self, field):
        self.field = field

    def build_new(self):
        self.__init__(True)

This will cause the field variable to be set to True over False. Basically, you are re-initializing the instance rather than creating a brand new one.

Your current code creates a new instance and just loses the reference to it when it goes out of scope (i.e. the function returning) because you are just rebinding the name of self to a different value not actually changing the inner contents of self.

Wednesday, September 21, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :