17

I know that it is possible to call a function with a variable number of parameters with call_user_func_array() found here -> http://php.net/manual/en/function.call-user-func-array.php . What I want to do is nearly identical, but instead of a function, I want to call a PHP class with a variable number of parameters in it's constructor.

It would work something like the below, but I won't know the number of parameters, so I won't know how to instantiate the class.

<?php
//The class name will be pulled dynamically from another source
$myClass = '\Some\Dynamically\Generated\Class';
//The parameters will also be pulled from another source, for simplicity I
//have used two parameters. There could be 0, 1, 2, N, ... parameters
$myParameters = array ('dynamicparam1', 'dynamicparam2');
//The instantiated class needs to be called with 0, 1, 2, N, ... parameters
//not just two parameters.
$myClassInstance = new $myClass($myParameters[0], $myParameters[1]);
hakre
  • 178,314
  • 47
  • 389
  • 754
bmarti44
  • 1,177
  • 2
  • 13
  • 22
  • Your question is a little ambiguous. It sounds like you're saying you don't know now how many arguments the constructor will take, but it is some amount which will be fixed at runtime, and you want to call it with the correct number. Is that correct? – Jason Burbage Jan 04 '12 at 22:00
  • @jburbage Yes... The name of the class to be instantiated and the parameters for the constructor WILL be defined. I am pulling that information from another source. For the sake of simplicity I left that out of this code. I'm going to comment the above code to clarify. – bmarti44 Jan 04 '12 at 22:06
  • 3
    Have you seen [this question](http://stackoverflow.com/questions/1569949/instantiating-a-new-php-class-with-one-or-many-arguments)? The answers there may solve your problem. – Jason Burbage Jan 04 '12 at 22:07

4 Answers4

50

You can do the following using ReflectionClass

$myClass = '\Some\Dynamically\Generated\a';
$myParameters = array ('dynamicparam1', 'dynamicparam2');

$reflection = new \ReflectionClass($myClass); 
$myClassInstance = $reflection->newInstanceArgs($myParameters); 

PHP manual: http://www.php.net/manual/en/reflectionclass.newinstanceargs.php

Edit:

In php 5.6 you can achieve this with Argument unpacking.

$myClass = '\Some\Dynamically\Generated\a';
$myParameters = ['dynamicparam1', 'dynamicparam2'];

$myClassInstance = new $myClass(...$myParameters); 
Community
  • 1
  • 1
satrun77
  • 3,122
  • 15
  • 19
  • This is actually what I ended up needing to use. I just wonder if using the ReflectionClass will end up slowing down my code a lot. – bmarti44 Jan 05 '12 at 15:28
  • 2
    @bmarti44 it depend on how you are going to use it! Here is two links about the performanceof ReflectionClass http://stackoverflow.com/questions/294582/php-5-reflection-api-performance and http://blog.liip.ch/archive/2008/09/18/fotd-reflectionclass-newinstanceargs-args-is-slow.html If it slowed your code performance, you could cache the class structure. – satrun77 Jan 05 '12 at 19:38
7

I implement this approach a lot when function args are > 2, rather then end up with an Christmas list of arguments which must be in a specific order, I simply pass in an associative array. By passing in an associative array, I can check for necessary and optional args and handle missing values as needed. Something like:

class MyClass
{
    protected $requiredArg1;
    protected $optionalArg1;

    public function __construct(array $options = array())
    {
        // Check for a necessary arg
        if (!isset($options['requiredArg1'])) {
            throw new Exception('Missing requiredArg1');
        }

        // Now I can just localize
        $requiredArg1 = $options['requiredArg1'];
        $optionalArg1 = (isset($options['optionalArg1'])) ? $options['optionalArg1'] : null;

        // Now that you have localized args, do what you want
        $this->requiredArg1 = $requiredArg1;
        $this->optionalArg1 = $optionalArg1;            
    }
}

// Example call
$class = 'MyClass';
$array = array('requiredArg1' => 'Foo!', 'optionalArg1' => 'Bar!');

$instance = new $class($array);

var_dump($instance->getRequiredArg1());
var_dump($instance->getOptionalArg1());

I highly recommend using an associative array, however it is possible to use a 0-index array. You will have to be extremely careful when constructing the array and account for indices that have meaning, otherwise you will pass in an array with offset args and wreck havoc with your function.

Mike Purcell
  • 19,055
  • 9
  • 47
  • 84
  • I would use this method, but. . . One of the classes being used requires two arguments, and was not written by me. . . So i'll need to go with the ReflectionClass. Thank you for this method though, i'll be sure we use it in all new classes created. – bmarti44 Jan 05 '12 at 15:30
  • I use this method a lot too, however, I only use the `$options` array for strictly _optional_ parameters and allow the _compiler_ to break on missing required arguments in the method call. – MrWhite May 14 '14 at 09:47
0

You can do that using func_get_args().

class my_class {
    function __construct( $first = NULL ) {
        $params = func_get_args();
        if( is_array( $first ) ) 
            $params = $first;
        // the $params array will contain the 
        // arguments passed to the child function
        foreach( $params as $p )
            echo "Param: $p\n";
    }
}
function my_function() {
    $instance = new my_class( func_get_args() );
}

echo "you can still create my_class instances like normal:";
$instance = new my_class( "one", "two", "three" );
echo "\n\n\n";
echo "but also through my_function:";
my_function( "one", "two", "three" );

Basically, you simply pass the result of func_get_args to the constructor of your class, and let it decide whether it is being called with an array of arguments from that function, or whether it is being called normally.

This code outputs

you can still create my_class instances like normal: 
Param: one
Param: two
Param: three

but also through my_function: 
Param: one
Param: two
Param: three

Hope that helps.

stew822
  • 1
  • 1
0

I've found here

Is there a call_user_func() equivalent to create a new class instance?

the example:

function createInstance($className, array $arguments = array())
{
    if(class_exists($className)) {
        return call_user_func_array(array(
            new ReflectionClass($className), 'newInstance'), 
            $arguments);
    }
    return false;
}

But can somebody tell me if there is an example for classes with protected constructors?

Community
  • 1
  • 1