International PHP Conference 2015

call_user_func_array

(PHP 4 >= 4.0.4, PHP 5)

call_user_func_arrayAppelle une fonction de rappel avec les paramètres rassemblés en tableau

Description

mixed call_user_func_array ( callable $callback , array $param_arr )

Appelle la fonction de rappel callback fournie avec les paramètres param_arr, rassemblés dans un tableau.

Liste de paramètres

callback

La fonction de rappel à appeler.

param_arr

Les paramètres à passer à la fonction de rappel, sous la forme d'un tableau indexé.

Valeurs de retour

Retourne la valeur retournée par la fonction de rappel, ou FALSE si une erreur survient.

Historique

Version Description
5.3.0 L'interprétation des mots-clés du modèle objet comme parent et self a changé. Avant, les appeler avec la syntaxe à double deux-points envoyait une erreur E_STRICT à cause de l'interprétation statique.

Exemples

Exemple #1 Exemple avec call_user_func_array()

<?php
function foobar($arg$arg2) {
    echo 
__FUNCTION__" got $arg and $arg2\n";
}
class 
foo {
    function 
bar($arg$arg2) {
        echo 
__METHOD__" got $arg and $arg2\n";
    }
}


// Appel de la fonction foobar() avec 2 arguments
call_user_func_array("foobar", array("one""two"));

// Appel de la méthode $foo->bar() avec 2 arguments
$foo = new foo;
call_user_func_array(array($foo"bar"), array("three""four"));
?>

L'exemple ci-dessus va afficher quelque chose de similaire à :

foobar got one and two
foo::bar got three and four

Exemple #2 Exemple avec call_user_func_array() en utilisant un espace de nom

<?php

namespace Foobar;

class 
Foo {
    static public function 
test($name) {
        print 
"Bonjour {$name}!\n";
    }
}

// Depuis PHP 5.3.0
call_user_func_array(__NAMESPACE__ .'\Foo::test', array('Hannes'));

// Depuis PHP 5.3.0
call_user_func_array(array(__NAMESPACE__ .'\Foo''test'), array('Philip'));

?>

L'exemple ci-dessus va afficher :

Bonjour Hannes!
Bonjour Philip!

Exemple #3 Utilisation d'une fonction lambda

<?php

$func 
= function($arg1$arg2) {
    return 
$arg1 $arg2;
};

var_dump(call_user_func_array($func, array(24))); /* Depuis PHP 5.3.0 */

?>

L'exemple ci-dessus va afficher :

int(8)

Notes

Note:

Avant PHP 5.4, les variables référencées dans le paramètre param_arr sont passées dans la fonction par référence, indépendamment du fait que la fonction s'attend à ce que le paramètre soit passé par référence. Le fait d'utiliser ce mode pour passer les valeurs au paramètre n'émet pas de notice, mais c'est tout de même obsolète et a été supprimé depuis PHP 5.4. En outre, ceci ne s'applique pas aux fonctions internes, qui voient leurs signatures de fonctions honorées. Le fait de passer une valeur lorsque la fonction s'attend à un paramètre passé par référence produit une alerte et la fonction call_user_func() retournera FALSE (il y a, cependant, une exception pour les valeurs passées par référence count = 1, comme en litéral, vu qu'elle peut être converti en référence sans problème, mais aussi parceque l'écriture sur ces valeurs n'a aucun effet - ne vous fiez pas à ce détail, sachant que son comportement est discutable).

Note:

Notez que les fonctions de rappel enregistrées avec des fonctions comme call_user_func() et call_user_func_array() ne seront pas appelées si une exception n'est pas interceptée alors qu'elle a été lancée dans une précédente fonction de rappel.

Voir aussi

add a note add a note

User Contributed Notes 27 notes

up
5
stanislav dot eckert at vizson dot de
1 year ago
Please note, that when calling call_user_func_array() to redirect parameters between inherited classes, you should not use $this, because $this always refers to the class which has been instantiated. The following code even seems to crash PHP (PHP does not report error but the process simply terminates), because the the parameters are redirected only one level up (to class foo_bar2):

<?php

   
class foo_bar1
   
{
        public function
__construct()
        {
            echo
__CLASS__ . PHP_EOL;

            if (
func_num_args() > 0)
            {
               
$constructorArgs = func_get_args();
               
call_user_func_array(array($this, 'parent::__construct'), $constructorArgs);
            }
            else
            {
               
parent::__construct();
            }
        }
    }

    class
foo_bar2 extends foo_bar1
   
{
        public function
__construct()
        {
            echo
__CLASS__ . PHP_EOL;

            if (
func_num_args() > 0)
            {
               
$constructorArgs = func_get_args();
               
call_user_func_array(array($this, 'parent::__construct'), $constructorArgs);
            }
            else
            {
               
parent::__construct();
            }
        }
    }

    class
foo_bar3 extends foo_bar2
   
{
        public function
__construct()
        {
            echo
__CLASS__ . PHP_EOL;

            if (
func_num_args() > 0)
            {
               
$constructorArgs = func_get_args();
               
call_user_func_array(array($this, 'parent::__construct'), $constructorArgs);
            }
            else
            {
               
parent::__construct();
            }
        }
    }

   
$f = new foo_bar3("abc");

?>

Instead, use the direct name of the class as string or, better, the magic constant __CLASS__ in call_user_func_array(), like:

    call_user_func_array(array(__CLASS__, 'parent::__construct'), $constructorArgs);

Then the parameters will be correctly redirected to the lowest base class.
up
4
dmitry dot revenko at businessmedia dot ru
5 years ago
Just hope this note helps someone (I killed the whole day on issue).

If you use something like this in PHP < 5.3:
<?php call_user_func_array(array($this, 'parent::func'), $args); ?>
Such a script will cause segmentation fault in your webserver.

In 5.3 you should write it:
<?php call_user_func_array('parent::func', $args); ?>
up
4
Damin
6 years ago
Those having the passing by reference issue can use this simple hack.
I´m really not sure WHY this works, but it does, and it does not make use of EVAL or other questionable functions.
<?php
   
function executeHook($name, $type='hooks'){
       
$args = func_get_args();
       
array_shift($args);
       
array_shift($args);
       
//Rather stupid Hack for the call_user_func_array();
       
$Args = array();
        foreach(
$args as $k => &$arg){
           
$Args[$k] = &$arg;
        }
       
//End Hack
       
$hooks = &$this->$type;
        if(!isset(
$hooks[$name])) return false;
       
$hook = $hooks[$name];
       
call_user_func_array($hook, $Args);
    }
?>

All it´s doing is copying the args ($args) into a new array ($Args) by reference, which i would think would be identical to the original array in every way (that matters).

Note the code here is an example of usage. The actual hack is denoted by comments.
If someone knows a better alternative, by all means, i would love to see it.
up
2
levi at alliancesoftware dot com dot au
8 years ago
Regarding the comments below about calling parent constructors:

PHP5 with E_STRICT no longer allows calls as below:

<?php
// 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

<?php
// Works fine
call_user_func_array(array($this, 'parent::__construct'), $args);
?>
up
2
jaxxed
3 years ago
For anyone looking for the means to test for the first parameter before passing to this function, look at the is_callable (http://php.net/manual/en/function.is-callable.php) variable handler.

<?php

$handler
= array( 'MyClass', 'MyMethod');
$params = array(1,2,3,4);

if (
is_callable($handler) ) { call_user_func_array( $handler , $params ); }

?>
up
1
rrant (at) gmail (dot) com
9 years ago
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.
up
1
Anonymous
9 years ago
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);
}
?>
up
1
amer at o2 dot pl
10 years ago
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
up
1
hong dot nguyen at k-edge dot com
11 years ago
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".
up
0
Anonymous
22 days ago
$param_arr may be empty, though it can't be null.

<?php
function foo( $first = 'default1', $second = 'default2' ) {
    echo
"first: '$first', second: '$second'\n";
}
call_user_func_array( 'foo', array( 'one', 'two' ) );
call_user_func_array( 'foo', array( 'only one' ) );
call_user_func_array( 'foo', array() );
call_user_func_array( 'foo', null );
?>

Output:
first: 'one', second: 'two'
first: 'only one', second: 'default2'
first: 'default1', second: 'default2'
/* error message or nothing printed depending on version */
up
0
admin at torntech dot com
1 month ago
As of PHP 5.6 you can utilize argument unpacking as an alternative to call_user_func_array, and is often 3 to 4 times faster.

<?php
function foo ($a, $b) {
     return
$a + $b;
}

$func = 'foo';
$values = array(1, 2);
call_user_func_array($func, $values);
//returns 3

$func(...$values);
//returns 3
?>

Benchmarks from https://gist.github.com/nikic/6390366
cufa   with 0 args took 0.43453288078308
switch with 0 args took 0.24134302139282
unpack with 0 args took 0.12418699264526
cufa   with 5 args took 0.73408579826355
switch with 5 args took 0.49595499038696
unpack with 5 args took 0.18640494346619
cufa   with 100 args took 5.0327250957489
switch with 100 args took 5.291127204895
unpack with 100 args took 1.2362589836121
up
0
Kris dot Craig at gmail dot com
5 years ago
Many people have wondered how to effectively implement dispatch tables in PHP.  Here's my answer to that (if you'll forgive my creative flair):

<?php

/*
* Using dispatch tables in PHP.
*
* --Kris Craig
*/

define( "YOUR_MOTHER"1 );
define( "YOUR_FATHER"2 );
define( "YOUR_BROTHER", 3 );
define( "YOUR_SISTER"4 );

class
MyFamily
{
    static
$dispatch = array( YOUR_MOTHER => "Mom", YOUR_FATHER => "GetPrisonInmate", YOUR_BROTHER => "ReplaceName", YOUR_SISTER => "LazyGirl" );
    static
$args = array( YOUR_MOTHER => array(), YOUR_FATHER => array( "55170-054", TRUE ), YOUR_BROTHER => array(), YOUR_SISTER => array() );
   
    function
GetDispatch( $fromwhere )
    {
        return
call_user_func_array( array( self, self::$dispatch[$fromwhere] ), self::$args[$fromwhere] );
    }
   
    function
Mom()
    {
        return
"Mommy loves you!";
    }
   
    function
GetPrisonInmate( $PrisonerID, $GoodBehavior )
    {
       
//Check prison records for his ID, then....
       
       
if ( $GoodBehavior )
        {
           
$parole = "APPROVED";
        }
        else
        {
           
$parole = "DENIED";
        }
       
        return
"Your father (#$PrisonerID) has $remaining years left in his sentence.  His most recent parole application has been:  $parole";
    }
   
    function
ReplaceName()
    {
        return
"Her name is Sally now.";
    }
   
    function
LazyGirl()
    {
        print
"Your sister needs to get out more....";
       
       
//sleep( pow( 60, 2 ) * 18 );  //You can sleep later!
       
       
die( "Nah, I'm too tired." );
    }
}

print
"Status on family member: " . MyFamily::GetDispatch( YOUR_FATHER );

?>
up
0
noone at example dot com
5 years ago
For those of you that have to consider performance: it takes about 3 times as long to call the function this way than via a straight statement, so whenever it is feasible to avoid this method it's a wise idea to do so.

Note that eval() is about 10 times slower than a straight statement to call a function with arguments, so this is definitely a better option than using eval() even if you only consider performance.
up
0
aj at ajbrown dot org
5 years ago
Just a heads up, the second parameter MUST be an array if it's specified,  but that doesn't seem to be enforced until ~5.3.

I just pulled my hair out with an old installation of CakePHP because it was passing NULL instead of an empty array.
up
0
thiago dot henrique dot mata at gmail dot com
7 years ago
<?php
Class Delegate
{
    private
$arrInstances = array();
   
    protected function
addObject( $oElement )
    {
       
// add one element on the end of the stack  //
       
$this->arrInstances[] = $oElement;
    }   
   
    public function
__call( $strMethod, $arrParams )
    {
       
// for each element in instance //
       
foreach( $this->arrInstances as $oElement )
        {
           
// get the class of the element //
           
$strClass = get_class( $oElement );
           
// get all methods of the class  //
           
$arrMethods = get_class_methods( $strClass );
           
// case the method exists into this class  //
           
if( in_array( $strMethod , $arrMethods ) )
            {
               
// prepare caller //
               
$arrCaller = Array( $strClass , $strMethod );
               
// return the result of the method into the object  //
               
return call_user_func_array( $arrCaller, $arrParams );
            }
        }
       
// any object has the method //
        // throw a exception //
       
throw new Exception( " Method " . $strMethod . " not exist in this class " . get_class( $this ) . "." );
    }
}

class
Log
{
    public function
sayHi()
    {
        print
"hi!" . "<br/>\n";
    }
   
    public function
sayMyName()
    {
        print
"log" . "<br/>\n";
    }
}

class
Other
{
    public function
sayHello()
    {
        print
"hello there!" . "<br/>\n";
    }
   
    public function
sayMyName()
    {
        print
"other" . "<br/>\n";
    }
}

class
Example extends Delegate
{
    public function
__construct()
    {
       
$this->addObject( new Log() );
       
$this->addObject( new Other() );
    }
}

$oExample = new Example();
$oExample->sayHi();
$oExample->sayHello();
$oExample->sayMyName();

/*
    hi!<br/>
    hello there!<br/>
    log<br/>
*/
?>
up
0
dnhuff at acm.org
7 years ago
It appears that when PHP executes something like:

$a = array(1,2,3);
$b =& $a[1];

both $b and $a[1] are converted into references to a common value -- makes sense until you transfer that to a call_user_func:

call_user_func_array('foo', $a);

suddenly, inside foo, the second parameter is passed by reference!

And you can't call this wrong, only another subtly of references.

Note it appears that ksort($a) will remove the reference as well as put the elements in key order so you (probably) get what you expect. (see below on the use of a foreach ($a as &v).)
up
0
aeolianmeson at 8n54tvv dot blitzeclipse dot com
7 years ago
There's a possibility that call_user_func_array(), call_user_func(), and Exception::getTrace() will cause a trace entry to not have the 'file' or 'line' elements.

Dustin Oprea
up
0
adamh at densi dot com
12 years ago
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.
up
-1
eugene at artprime dot ru
9 years ago
<?php
 
return call_user_func_array(
    array(new
ReflectionClass($className), 'newInstance'),
   
$functionParameters
 
);
?>

Look here: http://www.zend.com/zend/week/week182.php#Heading1
up
-1
Egor
9 years ago
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.
up
-1
james at gogo dot co dot nz
10 years ago
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;
    }
?>
up
-1
mrextreme at freemail dot hu
5 years ago
If you are using PHP < 5.3 and want to call the parent class' __construct() with a variable parameter list, use this:

<?php
public function __construct()
{
   
$vArgs = func_get_args(); // you can't just put func_get_args() into a function as a parameter
   
call_user_func_array(array('parent', '__construct'), $vArgs);
}
?>
up
-2
richard_harrison at rjharrison dot org
8 years ago
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');
?>
up
-2
crocodile2u at yandex dot ru
9 years ago
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;
}
?>
up
-2
taylor
10 years ago
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.
up
-3
Brad Proctor
4 years ago
This function is relatively slow (as of PHP 5.3.3) and if you are calling a method with a known number of parameters it is much faster to call it this way:

$class->{$method}($param1, $param2);

vs

call_user_func_array (array($class, $method), array($param1, $param2));

But if you don't know how many parameters...

The wrapper function below is slightly faster, but the problem now is that you are making two function calls.  One to the wrapper and one to the function.

However, If you are able to take this code out of the function and use it inline it is nearly twice as fast (in most cases) as calling call_user_func_array natively.

<?php
function wrap_call_user_func_array($c, $a, $p) {
    switch(
count($p)) {
        case
0: $c->{$a}(); break;
        case
1: $c->{$a}($p[0]); break;
        case
2: $c->{$a}($p[0], $p[1]); break;
        case
3: $c->{$a}($p[0], $p[1], $p[2]); break;
        case
4: $c->{$a}($p[0], $p[1], $p[2], $p[3]); break;
        case
5: $c->{$a}($p[0], $p[1], $p[2], $p[3], $p[4]); break;
        default:
call_user_func_array(array($c, $a), $p);  break;
    }
}
?>
up
-2
Freek (at) Gruntjes.net
6 years ago
I just noticed that when you use this function with parameters that need to be passed by reference it will not work.

<?php
function refFunc(&$var)
{
 
$var .= 'bar';
}

$var = 'foo';
?>

call_user_func_array('refFunc', array($var));
echo $var;

will output 'foo' and not  'foobar'. Witch is logical since you are declaring a new variable with array($var) however not so obvious.
To Top