Warum sollte man call_user_func_array dem regulären Funktionsaufruf vorziehen?

Lesezeit: 8 Minuten

Benutzer-Avatar
Prinz Singh

function foobar($arg, $arg2) {
    echo __FUNCTION__, " got $arg and $arg2\n";
}
foobar('one','two'); // OUTPUTS : foobar got one and two 

call_user_func_array("foobar", array("one", "two")); // // OUTPUTS : foobar got one and two 

Wie ich sowohl normale als auch sehen kann call_user_func_array Methode beide Ausgänge gleich, warum sollte man sie dann bevorzugen?

In welchem ​​​​Szenario wird die reguläre Aufrufmethode jedoch fehlschlagen call_user_func_array wird nicht?

Kann ich ein solches Beispiel bekommen?

Vielen Dank

  • call_user_func_array wird hauptsächlich in dynamischen Kontexten verwendet, wenn Sie nicht wissen, welche Funktion Sie aufrufen und welche Argumente Sie vorher übergeben werden.

    – Strickl

    30. August 2013 um 6:07 Uhr

  • Können wir nicht die reguläre Methode für ein Array von Argumenten verwenden? @elclans

    – Prinz Singh

    30. August 2013 um 6:09 Uhr

Benutzer-Avatar
verzeihen

  1. Sie haben ein Array mit den Argumenten für Ihre Funktion, das eine unbestimmte Länge hat.

    $args = someFuncWhichReturnsTheArgs();
    
    foobar( /* put these $args here, you do not know how many there are */ );
    

    Die Alternative wäre:

    switch (count($args)) {
        case 1:
            foobar($args[0]);
            break;
        case 2:
            foobar($args[0], $args[1]);
            break;
        ...
    }
    

    Was keine Lösung ist.

Der Anwendungsfall dafür mag selten sein, aber wenn Sie darauf stoßen, sind Sie es brauchen es.

  • Dieses spezielle Problem wird jetzt in PHP 5.6 durch das Entpacken von Argumenten gelöst: foobar(...$args);

    – Verärgerte Ziege

    6. Juli 2015 um 23:04 Uhr

  • Was wird die Argumentliste in der Definition von sein foobar Funktion ?

    – Istiaque Ahmed

    29. September 2017 um 19:29 Uhr

  • @IstiaqueAhmed Angenommen, es wird verwendet func_get_args() der diskussion wegen.

    – verzeihen

    29. September 2017 um 19:30 Uhr

  • Dies ist nicht die übliche Art, eine Funktion zu deklarieren. Die Antwort erwähnt es auch nicht und macht die Sache undurchsichtig.

    – Istiaque Ahmed

    29. September 2017 um 19:34 Uhr

  • @IstiaqueAhmed Es spielt keine Rolle, wie die Funktion implementiert wird. Sie können auch davon ausgehen, dass alle Parameter optional sind und/oder die Funktion (Name) selbst eine Variable ist. Hier geht es ausschließlich um Variablenargumente Anrufe.

    – verzeihen

    29. September 2017 um 19:38 Uhr


Benutzer-Avatar
Jack

In welchem ​​​​Szenario schlägt die reguläre Aufrufmethode fehl, aber call_user_func_array nicht?

Wenn Sie vorher nicht wissen, wie viele Argumente Sie an Ihre Funktion übergeben werden, wäre es ratsam, sie zu verwenden call_user_func_array(); die einzige alternative ist a switch Anweisung oder eine Reihe von Bedingungen, um eine vordefinierte Teilmenge von Möglichkeiten zu erreichen.

Ein anderes Szenario ist, wo die aufzurufende Funktion vorher nicht bekannt ist, z array($obj, 'method'); hier könntest du auch gebrauchen call_user_func().

$fn = array($obj, 'method');
$args = [1, 2, 3];
call_user_func_array($fn, $args);

Beachten Sie, dass mit call_user_func_* Funktionen können nicht verwendet werden, um private oder geschützte Methoden aufzurufen.

Die Alternative zu all dem besteht darin, Ihre Funktionen dazu zu bringen, ein Array als einziges Argument zu akzeptieren:

myfn([1, 2, 3]);

Dies eliminiert jedoch die Möglichkeit, jedes Argument in Ihrer Funktionsdeklaration mit einem Tipp zu versehen, und wird im Allgemeinen als Codegeruch angesehen.

  • If you don't know beforehand how many arguments you're going to pass to your function, it would be advisable to use call_user_func_array(); – Können Sie mit relevantem Codebeispiel näher darauf eingehen?

    – Istiaque Ahmed

    16. Oktober 2017 um 8:42 Uhr

  • @IstiaqueAhmed das Beispiel wird einmal abwärts gegeben, hast du etwas anderes erwartet?

    – Jack

    16. Oktober 2017 um 8:56 Uhr

  • Nennen Sie uns die Definition von $obj und sein method auch.

    – Istiaque Ahmed

    16. Oktober 2017 um 9:16 Uhr

  • @IstiaqueAhmed $obj hat eine Funktion namens method das braucht drei Argumente …

    – Jack

    16. Oktober 2017 um 9:17 Uhr

  • Sie wissen also, dass Sie 3 Argumente übergeben müssen. Also, welche Rolle spielt call_user_func_array Spiele hier ?

    – Istiaque Ahmed

    16. Oktober 2017 um 9:19 Uhr

Sie sollten die Funktion lieber wie gewohnt aufrufen. Verwenden call_user_func_array mit dynamischen Argumenten. Zum Beispiel:

function func(arg1, arg2, arg3) {
  return "$arg1, $arg2, $arg3";
}

func(1, 2, 3); //=> "1, 2, 3"

$args = range(5,7); // dynamic arguments
call_user_func_array('func', $args); //=> "5, 6, 7"

  • Was passiert, wenn die Länge des Arrays nicht mit der Anzahl der Parameter in der Funktion übereinstimmt und die Funktion keine Standardwerte verwendet? Ein Fehler? Oder werden die anderen Argumente einfach null (wenn zu wenige) oder ignoriert (wenn zu viele)?

    – Mr_Moneybags

    17. November 2016 um 0:11 Uhr

  • kannst du näher erläutern, was du meinst dynamic arguments ?

    – Istiaque Ahmed

    16. Oktober 2017 um 8:37 Uhr

Benutzer-Avatar
Warbo

call_user_func_array führt “uncurrying” durch, was das Gegenteil von “currying” ist.

Das Folgende gilt für alle “Callables” von PHP (benannte Funktionen, Closures, Methoden, __invokeetc.), also ignorieren wir der Einfachheit halber die Unterschiede und konzentrieren uns nur auf Closures.

Wenn wir mehrere Argumente akzeptieren wollen, lässt uns PHP dies mit 3 verschiedenen APIs tun. Der übliche Weg ist dieser:

$usual = function($a, $b, $c, $d) {
             return $a + $b + $c + $d;
         };
$result = $usual(10, 20, 30, 40);  // $result == 100

Ein anderer Weg heißt Curry bilden:

$curried = function($a) {
               return function($b) use ($a) {
                          return function($c) use ($a, $b) {
                                     return function($d) use ($a, $b, $c) {
                                                return $a + $b + $c + $d;
                                            };
                                 };
                      };
           };
$result = call_user_func(
              call_user_func(
                  call_user_func(
                      $curried(10),
                      20),
                  30),
              40);  // $result == 100

Der Vorteil ist, dass alle Curry-Funktionen auf die gleiche Weise aufgerufen werden können: Geben Sie ihnen ein Argument.

Wenn mehr Argumente erforderlich sind, werden mehr Curry-Funktionen zurückgegeben, die sich an die vorherigen Argumente „erinnern“. Dies ermöglicht es uns, einige Argumente jetzt und den Rest später zu übergeben.

Dabei gibt es einige Probleme:

  • Offensichtlich ist es sehr mühsam, Funktionen auf diese Weise zu schreiben und aufzurufen.
  • Wenn wir Curry-Funktionen bereitstellen, werden sie umständlich sein, wenn ihre „Gedächtnis“-Fähigkeit nicht benötigt wird.
  • Wenn wir uns auf die „Memory“-Fähigkeit von Curry-Funktionen verlassen, werden wir enttäuscht sein, wenn der Code anderer Leute sie nicht bereitstellt.

Wir können all diese Probleme beheben, indem wir a verwenden Konvertierungsfunktion (Haftungsausschluss: Das ist mein Blog). Dadurch können wir unsere Funktionen auf die übliche Weise schreiben und aufrufen, geben ihnen jedoch die gleiche “Speicherfähigkeit”, als wären sie Curry:

$curried = curry(function($a, $b, $c, $d) {
                     return $a + $b + $c + $d;
                 });
$result1 = $curried(10, 20, 30, 40);  // $result1 = 100
$result2 = call_user_func($curried(10, 20), 30, 40); // $result2 = 100

Der dritte Weg heißt uncurry und nimmt alle seine Argumente in einem zusammen:

$uncurried = function($args) {
                 return $args[0] + $args[1] + $args[2] + $args[3];
             };
$result = $uncurried([10, 20, 30, 40]);  // $result == 100

Genau wie bei Curry-Funktionen können alle Funktionen ohne Curry mit einem Argument aufgerufen werden, obwohl es diesmal ein Array ist. Wir haben immer noch die gleichen Kompatibilitätsprobleme wie Curry-Funktionen: Wenn wir uns dafür entscheiden, nicht-Curry-Funktionen zu verwenden, können wir uns nicht darauf verlassen, dass alle anderen dasselbe wählen. Daher benötigen wir auch eine Umrechnungsfunktion für das Uncurrying. Das ist, was call_user_func_array tut:

$uncurried = function($args) use ($usual) {
                 return call_user_func_array($usual, $args);
             };
$result1 = $usual(10, 20, 30, 40);  // $result1 = 100
$result2 = $uncurried([10, 20, 30, 40]); // $result2 = 100

Interessanterweise können wir dieses Extra loswerden function($args) Wrapper (ein Prozess, der als “Eta-Reduktion” bekannt ist) durch Currying call_user_func_array:

$uncurried = curry('call_user_func_array', $usual);

$result = $uncurried([10, 20, 30, 40]); // $result == 100

Leider call_user_func_array ist nicht so schlau wie curry; es wird nicht automatisch zwischen den beiden konvertiert. Wir können unsere eigenen schreiben uncurry Funktion, die diese Fähigkeit hat:

function uncurry($f)
{
    return function($args) use ($f) {
               return call_user_func_array(
                          $f,
                          (count(func_get_args()) > 1)? func_get_args()
                                                      : $args);
           };
}

$uncurried = uncurry($usual);
$result1 = $uncurried(10, 20, 30, 40); // $result1 == 100
$result2 = $uncurried([10, 20, 30, 40]); // $result2 == 100

Diese Konvertierungsfunktionen zeigen, dass die „übliche“ Art von PHP, Funktionen zu definieren, tatsächlich überflüssig ist: Wenn wir die „üblichen“ Funktionen von PHP durch „intelligente“ Curry- oder Uncurry-Funktionen ersetzen würden, würde eine Menge Code weiterarbeiten. Wenn wir das getan haben, ist es besser, alles zu curryen und nach Bedarf selektiv zu entfernen, da dies einfacher ist als umgekehrt.

Leider einige Dinge, die eine variable Anzahl von Argumenten verwenden func_get_args würde brechen, sowie Funktionen mit Standardargumentwerten.

Interessanterweise sind Standardwerte nur eine spezielle Form des Currys. Wir könnten meistens darauf verzichten, wenn wir diese Argumente vorbringen Erste statt zuletzt und stellte eine Reihe alternativer Definitionen bereit, die in den Standardeinstellungen enthalten sind. Zum Beispiel:

$defaults = function($a, $b, $c = 30, $d = 40) {
                return $a + $b + $c + $d;
            };
$def1 = $defaults(10, 20, 30, 40);  // $def1 == 100
$def2 = $defaults(10, 20, 30);      // $def2 == 100
$def3 = $defaults(10, 20);          // $def3 == 100

$curried = function($d, $c, $a, $b) {
               return $a + $b + $c + $d;
           };
$curriedD  = $curried(40);
$curriedDC = $curriedD(30);

$cur1 = $curried(10, 20, 30, 40);  // $cur1 == 100
$cur2 = $curriedD(10, 20, 30);     // $cur2 == 100
$cur3 = $curriedDC(10, 20);        // $cur3 == 100

Benutzer-Avatar
Thoracius Appotit

Ab PHP 5.6, um eine zu übergeben Reihe statt ein Argumentliste zu einer Funktion stellen Sie dem Array einfach ein Auslassungszeichen voran (dies wird als “Argument-Entpacken” bezeichnet).

function foo($var1, $var2, $var3) {
   echo $var1 + $var2 + var3;
}

$array = [1,2,3];

foo(...$array);  // 6
// same as call_user_func_array('foo',$array);

Der Unterschied zwischen call_user_func_array() und variable Funktionen Ab PHP 5.6 erlauben Variablenfunktionen nicht den Aufruf einer statischen Methode:

$params = [1,2,3,4,5];

function test_function() {
  echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n";
}    

// Normal function as callback
$callback_function = 'test_function';
call_user_func_array($callback_function,$params); // 1+2+3+4+5=15
$callback_function(...$params); // 1+2+3+4+5=15

class TestClass
{
  static function testStaticMethod() {
    echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n";
  }

  public function testMethod() {
    echo implode('+',func_get_args()) .'='. array_sum(func_get_args())."\r\n";
  }
}

// Class method as callback
$obj = new TestClass;
$callback_function = [$obj,'testMethod'];
call_user_func_array($callback_function,$params); // 1+2+3+4+5=15
$callback_function(...$params); // 1+2+3+4+5=15

// Static method callback
$callback_function = 'TestClass::testStaticMethod';
call_user_func_array($callback_function,$params); // 1+2+3+4+5=15
$callback_function(...$params); // Fatal error: undefined function

PHP 7 fügt die Möglichkeit hinzu, statische Methoden über eine Variablenfunktion aufzurufen, also ab PHP 7 Dies Unterschied besteht nicht mehr. Abschließend, call_user_func_array() gibt Ihrem Code mehr Kompatibilität.

<?php

class Demo {

    public function function1() {
        echo 'in function 1';
    }
}

$obj = new Demo();

$function_list = get_class_methods('Demo');

print_r($function_list);  //Array ( [0] => function1 )

call_user_func_array(array($obj, $function_list[0]), array()); 

// Output => in function 1

?>

1310780cookie-checkWarum sollte man call_user_func_array dem regulären Funktionsaufruf vorziehen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy