Page 1 of 1

Calling constructor \w variable length arg-list dynamically

Posted: Fri Aug 11, 2006 8:46 am
by Ollie Saunders
OK I want to instantiate a class with a parameterized constructed via the method of another class. Normally you just use call_user_func_array() and be done with it but that doesn't seem to work for constructors.

Code: Select all

<?php
class ComplexClass_A
{
    public $result;
    public function __construct($a, $b, $c)
    {
        $this->result = $a + $b + $c;
    }
    public static function init($a, $b, $c)
    {
        $instance = new self;
        $instance->result = $a + $b + $c;
        return $instance;
    }
}

class ComplexFactory
{
    public function factory()
    {
        $params = func_get_args();
        $name = 'ComplexClass_' . $params[0];
        unset($params[0]);

        /* Warning: call_user_func_array(): First argument is expected to be a
        valid callback, 'ComplexClass_A' was given */
        return call_user_func_array($name, $params);

        /* Strict Standards: Non-static method ComplexClass_A::__construct()
        cannot be called statically */
        // In addition to being an E_STRICT this also returns NULL
        return call_user_func_array(array($name, '__construct'), $params);

        // This works but its pretty manky and would take ages to implement completely
        $sQuot = '\'';
        foreach ($params as $k => $v) {
            if (is_string($v)) {
                $params[$k] = $sQuot . addcslashes($v, $sQuot) . $sQuot;
            } else if (is_array($v)) {
                // need recursion now to generate array() structure, ughewk i say.
            }
        }
        return eval('return new '. $name . '(' . implode(',', $params) . ');');

        // Finally, don't use the constructor at all not so great but also works
        return call_user_func_array(array($name, 'init'), $params);
    }
}

$factory = new ComplexFactory();
$a = $factory->factory('A', 1, 2, 3);
echo $a->result; // Desired: 6
Two quesions:
  • Which is best?
  • Does anyone have better?

Posted: Fri Aug 11, 2006 9:31 am
by volka
Reflection:

Code: Select all

<?php
class foobar {
	public function __construct($a, $b, $c) {
		echo $a,$b,$c;
	}
}

$arr = array('a',':', 'b');
$rc = new ReflectionClass('foobar');
$o = $rc->newInstanceArgs($arr);
?>
simple new-call

Code: Select all

<?php
class foobar {
	public function __construct($a, $b, $c) {
		echo $a,$b,$c;
	}
}

$class = 'foobar';
$o = new $class('a',':', 'b');
?>

Posted: Fri Aug 11, 2006 10:27 am
by Ollie Saunders
OK sorry I forgot to specify the __construct() has a variable number of arguments. Neither of those work.

Code: Select all

$reflection = new ReflectionClass($name);
$constructor = $reflection->getConstructor();
// requires a 1nd arg of object instance, which obviously doesn't exist 
// yet because its a constructor
return $constructor->invokeArgs(null, $params);

Code: Select all

$reflection = new ReflectionClass($name);
// Passes a single parameter, $params array to __construct().
// not several parameters
return $reflection->newInstance($params);
// This is what I want, if it existed 
return $reflection->newInstanceArray($params);

Code: Select all

$o = $rc->newInstanceArgs($arr);
No such method

Posted: Fri Aug 11, 2006 12:44 pm
by Ollie Saunders
OK I was being quite ridicious with the eval method.
This is all you have to do:

Code: Select all

$params = func_get_args();
$className = array_shift($params);
$paramsStr = '$this->_mask,$this->_array';
foreach ($params as $k => $v) {
    $paramsStr .= ',$params[' . $k . ']';
}
return eval('return new '. $className . '(' . $paramsStr . ');');
Which makes eval the winner! :D

Posted: Fri Aug 11, 2006 2:15 pm
by volka
ole wrote:

Code: Select all

$o = $rc->newInstanceArgs($arr);
No such method
sppoky. php version?
ole wrote:OK sorry I forgot to specify the __construct() has a variable number of arguments.
But they are handled by the same factory..?

Posted: Fri Aug 11, 2006 2:19 pm
by Ollie Saunders
Dw about it volka. I've just had a change of design and now no longer need this.