|
call_user_func_array
Call a user function given with an array of parameters
(PHP 4 >= 4.0.4, PHP 5)
Example 752. call_user_func_array() example<?php Related Examples ( Source code ) » call_user_func_array Examples ( Source code ) » __call Code Examples / Notes » call_user_func_arraynospam dot dont dot remove
Unified constructors in PHP4: <?php class Object { function Object() { $args= func_get_args(); call_user_func_array(array(&$this, '__construct'), $args); } function __construct($args= NULL) { var_dump($args); } } class Exception extends Object { var $message; function __construct($message) { $this->message= $message; parent::__construct(); } } class IOException extends Exception { } var_dump( error_reporting(), zend_version(), phpversion() ); $c= new IOException('file not found'); echo '===> Result: '; var_dump($c); ?> nutbar
This function, combined with some others, can allow you to make some really easy & generic function macros (since real macros don't exist in PHP yet). <?php function my_printf() { $args = func_get_args(); return call_user_func_array('printf', $args); } ?> This allows you to use my_printf() just as if it were printf() itself. Very useful since you don't have to worry about how many arguments any functions take. php
There is weird behavior on this function in terms of passing variables by reference. It seems that despite what the manual says, if a reference to the variable exists it will be passed by reference. For instance, take the following code: <?php function setargs(&$a1, &$a2, &$a3){ $a1 = "one"; $a2 = "two"; $a3 = "three"; } $args = array('1','2','3'); call_user_func_array('setargs', $args); echo implode(' ', $args); ?> The above code will display "1 2 3", indicating that the variables were passed by value. However, take the following code: <?php function setargs(&$a1, &$a2, &$a3){ $a1 = "one"; $a2 = "two"; $a3 = "three"; } $args = array('1','2','3'); $hack = array(); for($i = 0; $i < count($args); $i++){ $hack[] =& $args[$i]; } call_user_func_array('setargs', $args); echo implode(' ', $args); ?> Which displays "one two three", instead of the expected "1 2 3". This appears to be a bug in PHP, but it is a useful one because it makes it possible to pass variables by reference to a user function call. Of course it is dangerous to rely on a hack, especially one that only works because of a bug, but if you know what you are doing this could help you get around this limitation of call_user_func_array. levi
Regarding the comments below about calling parent constructors: PHP5 with E_STRICT no longer allows calls as below: <? // Causes an error with E_STRICT call_user_func_array(array('parent', '__construct'), $args); ?> It gives an error because you are trying to call a nonstatic function as if it was static. The correct syntax is <? // Works fine call_user_func_array(array($this, 'parent::__construct'), $args); ?> amer
PLS notice that "patripaq at hotmail dot com" 's code will be valid if B EXTENDS A... <?php class B extends A{ ... } ?> there>>"What I wanted to do is create an object that can manage any number and any kind of parameters." BUT IT IS NOT A POINT AT ALL If you need to call just function with parameters: call_user_func_array('Foo',$args); If you need to call CLASS method (NOT object): call_user_func_array(array('class', 'Foo'),$args); If you need to call OBJECT method: call_user_func_array(array(&$Object, 'Foo'),$args); If you need to call method of object of object: call_user_func_array(array(&$Object->Object, 'Foo'),$args); If you need to call object method from within the very same object (NOT CLASS!): call_user_func_array(array(&$this, 'Foo'),args); The call_user_func_array ITSELF can manage any number and any kind of parameters. It can handle ANY FUNCTION too as it is defined and that maybe partipaq wanted to manage. What You actually need is object composition not inheritance. Make an instance from arguments. <?php ... class B{ function __construct() { $args = func_get_args(); // Get arguments $this->OBJ = new A($args); call_user_func_array(array(&$this->OBJ, 'A'), $args ); } } ?> Then there can be any number and any type of created object B parameters egor
Note that, despite the name, this does work on builtin functions (and object methods with the array(&$obj, $method) syntax), not just user-defined functions and methods.
php
Note that, although it doesn't say so here or in the linked page with information about the callback type, the changelog ( http://www.php.net/ChangeLog-4.php#4.0.5 ) states that call_user_func_array in PHP 4.0.4 won't take an array as the first argument. This was added in 4.0.5.
rrant
Just an extra for the post of amer at o2 dot pl: If you need to call the PARENT method: call_user_func_array(array('parent', 'method'), $args); With that, if you need to call a constructor and/or add some extra code to the instantiation process: <?php function __construct() { // Get the arguments $args = func_get_args(); // Initialize parent with arguments call_user_func_array(array('parent', '__construct'), $args); // ... Your Code Here ... } ?> Note that your constructor pass all the arguments to the parent constructor and it doesn't matter how many arguments you pass. This is pretty useful for constructors with a variable number of arguments. gord
If you need to call object and class methods in PHP < 4.0.4, the following code ought to do the trick: <?php if (!function_exists('call_user_func_array')) { function call_user_func_array($func, $args) { $argString = ''; $comma = ''; for ($i = 0; $i < count($args); $i ++) { $argString .= $comma . "\$args[$i]"; $comma = ', '; } if (is_array($func)) { $obj =& $func[0]; $meth = $func[1]; if (is_string($func[0])) { eval("\$retval = $obj::\$meth($argString);"); } else { eval("\$retval = \$obj->\$meth($argString);"); } } else { eval("\$retval = \$func($argString);"); } return $retval; } } ?> richard_harrison
If you are thinking of using call_user_func_array to instantiate an object (see comments below using Reflection) then since v5.1.3 you can use the Reflection::newInstanceArgs() method. <?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'); ?> patripaq
I just started using PHP 5.0 and, so far, I'm loving it ! However, I had a problem the other day and thought it would be a good idea to inform other programmers about the solution I found to get around it. It concerns the new __constructor() function and the call_user_func_array() function. What I wanted to do is create an object that can manage any number and any kind of parameters. Here's the problematic code: <?php //-------------------------- class A { function __construct() { $args = func_get_args(); // Get arguments // Class initialization... } function A() { $args = func_get_args(); // Get arguments call_user_func_array( array(&$this, '__construct'), $args ); // Infinite loop to B::__construct()... } } class B { function __construct() { $args = func_get_args(); // Get arguments call_user_func_array( array(&$this, 'A'), $args ); // Initialize parent with arguments // Class initialization... } } $obj = new B( 'param1', 'param2' ); //-------------------------- ?> I suppose you can guess where the problem is located... In the A::A() function, the call to __construct() using call_user_func_array() is redirected to B::__construct() instead of A::__construct(). The only way I found to specify which constructor function I wanted to call was to stop using A::__construct() and use the old fashion constructor instead. If anyone can find a better way, feel free to add comments. Here's my solution. Hope it helps anyone. <?php //-------------------------- class A { function A() { $args = func_get_args(); // Get arguments // Class initialization... } } class B { function __construct() { $args = func_get_args(); // Get arguments call_user_func_array( array(&$this, 'A'), $args ); // Initialize parent with arguments // Class initialization... } } $obj = new B( 'param1', 'param2' ); //-------------------------- ?> taylor
I came up with a better solution to the problem that I solve below with createObjArray that maintains parameter type: <?php function createObjArray($type,$args=array()) { $paramstr = ''; for ($i = 0; $i < count($args); $i++) { $paramstr .= '$args['.$i.'],'; } $paramstr = rtrim($paramstr,','); return eval("return new $type($paramstr);"); } ?> Would be good to add error checking, but it works. crocodile2u
Here is another version of createObjArray() function written here earlier by taylor. Believing that using 'eval()' is at least "dirty", I came to the following solution (with a help of panchous - at phpclub dot ru forums ). This solution utilizes the new Reflection API. <?php function & createObjArray($type, $args = array()) { $reflection = new ReflectionClass($type); $output = call_user_func_array(array(&$reflection, 'newInstance'), $args); return $output; } ?> mightye
From what I can tell, call_user_func_array *always* passes by value, even if the function asks for a reference (perhaps it duplicates the args before passing them?).
15-may-2006 10:43
For those wishing to implement call-by-name functionality in PHP, such as implemented e.g. in DB apis, here's a quick-n-dirty version for PHP 5 and up <?php /** * Call a user function using named instead of positional parameters. * If some of the named parameters are not present in the original function, they * will be silently discarded. * Does no special processing for call-by-ref functions... * @param string $function name of function to be called * @param array $params array containing parameters to be passed to the function using their name (ie array key) */ function call_user_func_named($function, $params) { // make sure we do not throw exception if function not found: raise error instead... // (oh boy, we do like php 4 better than 5, don't we...) if (!function_exists($function)) { trigger_error('call to unexisting function '.$function, E_USER_ERROR); return NULL; } $reflect = new ReflectionFunction($function); $real_params = array(); foreach ($reflect->getParameters() as $i => $param) { $pname = $param->getName(); if ($param->isPassedByReference()) { /// @todo shall we raise some warning? } if (array_key_exists($pname, $params)) { $real_params[] = $params[$pname]; } else if ($param->isDefaultValueAvailable()) { $real_params[] = $param->getDefaultValue(); } else { // missing required parameter: mark an error and exit //return new Exception('call to '.$function.' missing parameter nr. '.$i+1); trigger_error(sprintf('call to %s missing parameter nr. %d', $function, $i+1), E_USER_ERROR); return NULL; } } return call_user_func_array($function, $real_params); } ?> adamh
call_user_func_array() is nifty for calling PHP functions which use variable argument length. For example: <?php $array = array( array("foo", "bar"), array("bat", "rat"), ); $values = call_user_func_array("array_merge", $array); var_dump($values); ?> /* output: array(4) { [0]=> string(3) "foo" [1]=> string(3) "bar" [2]=> string(3) "bat" [3]=> string(3) "rat" } */ The neat feature is that $array could have any number of arrays inside it. hong dot nguyen
call_user_func_array can pass parameters as reference: <?php call_user_func_array(array(&$obj,$method),array(&$arg1,$arg2,$arg3)) ?> Use it as work-around for "Call-time pass-by-reference has been deprecated". james
Be aware the call_user_func_array always returns by value, as demonstrated here... <?php function &foo(&$a) { return $a; } $b = 2; $c =& call_user_func_array('foo', array(&$b)); $c++; echo $b . ' ' . $c; ?> outputs "2 3", rather than the expected "3 3". Here is a function you can use in place of call_user_func_array which returns a reference to the result of the function call. <?php function &ref_call_user_func_array($callable, $args) { if(is_scalar($callable)) { // $callable is the name of a function $call = $callable; } else { if(is_object($callable[0])) { // $callable is an object and a method name $call = "\$callable[0]->{$callable[1]}"; } else { // $callable is a class name and a static method $call = "{$callable[0]}::{$callable[1]}"; } } // Note because the keys in $args might be strings // we do this in a slightly round about way. $argumentString = array(); $argumentKeys = array_keys($args); foreach($argumentKeys as $argK) { $argumentString[] = "\$args[$argumentKeys[$argK]]"; } $argumentString = implode($argumentString, ', '); // Note also that eval doesn't return references, so we // work around it in this way... eval("\$result =& {$call}({$argumentString});"); return $result; } ?> sc00bz
After looking at "php at paulbutler dot org" comment I tested the hack with a pass by value function just because I thought it might do something wrong, well it did. <?php function setargs($a1, $a2, $a3) // all are pass by value { $a1 = "one"; $a2 = "two"; $a3 = "three"; } $args = array("1","2","3"); $hack = array(); foreach ($args as &$v) $hack[] =& $v; call_user_func_array("setargs", $args); echo implode(" ", $args); ?> Displays "one two three", instead of the expected "1 2 3". Someone should really test this on the current version of php and report this bug. eugene
<?php return call_user_func_array( array(new ReflectionClass($className), 'newInstance'), $functionParameters ); ?> Look here: http://www.zend.com/zend/week/week182.php#Heading1 taylor
<?php /** * Create an object of a specified type using an array as the parameters * to the constructor. NOTE: does not maintain proper * types for the arguments. They are all converted to strings. * @param $type Type type of object to create (class name) * @param $args The arguments to pass to the constructor */ function createObjArray($type, $args=array()) { if ( !class_exists($type) ) { return NULL; } // build argument list; be sure to escape string delimeters $func = create_function('$str', 'return str_replace("\'","\\\'",$str);'); $sargs = "'" . join( "','", array_map($func,$args) ). "'"; // build & eval code; return result $seval = "return new $type($sargs);"; return eval($seval); } ?> |