Überladung der PHP-Funktion


195

Aus C ++ Hintergrund;)
Wie kann ich PHP-Funktionen überladen?

Eine Funktionsdefinition, wenn Argumente vorhanden sind, und eine andere, wenn keine Argumente vorhanden sind? Ist es in PHP möglich? Oder sollte ich if else verwenden, um zu überprüfen, ob Parameter von $ _GET und POST übergeben wurden? und sie in Beziehung setzen?


1
Sie können nur Klassenmethoden überladen, aber keine Funktionen. Siehe php.net/manual/en/language.oop5.overloading.php
Spechal

1
Sie können eine Funktion erstellen, die die Anzahl der Argumente explizit überprüft und eine andere Funktion aus einer vordefinierten Menge von Argumenten ausführt. Wie auch immer, Sie sollten Ihre Lösung besser neu entwerfen oder Klassen verwenden, die Ihre Schnittstelle implementieren
kolypto

2
Wie in php.net/manual/en/language.oop5.overloading.php angegeben , unterscheidet sich die Definition von Überladung durch PHP von der typischen OOP-Sprache. Sie beziehen sich nur auf magische Methoden, die ein dynamisches Routing von Eigenschaften und Funktionen basierend auf X ermöglichen.
Edwin Daniels

Für zukünftige Leser: Was sich auf @Spechal bezieht, ist eine andere Bedeutung für das Wort overloadingals in der Frage gestellt. (Siehe die akzeptierte Antwort für weitere Details.)
ToolmakerSteve

2
Hat sich seit PHP 7 etwas geändert? : o
nawfal

Antworten:


217

Sie können PHP-Funktionen nicht überladen. Funktionssignaturen basieren nur auf ihren Namen und enthalten keine Argumentlisten. Sie können also nicht zwei Funktionen mit demselben Namen haben. Das Überladen von Klassenmethoden unterscheidet sich in PHP von vielen anderen Sprachen. PHP verwendet dasselbe Wort, beschreibt jedoch ein anderes Muster.

Sie können jedoch eine variable Funktion deklarieren , die eine variable Anzahl von Argumenten akzeptiert . Sie würden func_num_args()und verwenden func_get_arg(), um die Argumente zu übergeben, und sie normal verwenden.

Beispielsweise:

function myFunc() {
    for ($i = 0; $i < func_num_args(); $i++) {
        printf("Argument %d: %s\n", $i, func_get_arg($i));
    }
}

/*
Argument 0: a
Argument 1: 2
Argument 2: 3.5
*/
myFunc('a', 2, 3.5);

8
Vielleicht habe ich zu viel C ++ entwickelt, aber ich würde einen Hinweis vorschlagen, dass dies in den Funktionsparametern wie gemacht wird myFunc(/*...*/).
Doug65536

4
@ doug65536, PHP 5.6+ unterstützt dieses "..." als Syntax-Token , zu unserer großen Erleichterung. ;)
Sz.

Oder sehen Sie Adils Antwort , die näher an der Überladung von C ++ liegt - so nah wie möglich - in einer locker getippten Sprache wie PHP. Dies ist in PHP 7 noch besser geeignet, da Sie Typhinweise für Parameter bereitstellen können, wenn diese in all Ihren Überladungen vom gleichen Typ sind.
ToolmakerSteve

78

PHP unterstützt keine herkömmliche Methodenüberladung. Eine Möglichkeit, das zu erreichen, was Sie möchten, besteht darin, die zu verwenden __call magische Methode zu verwenden:

class MyClass {
    public function __call($name, $args) {

        switch ($name) {
            case 'funcOne':
                switch (count($args)) {
                    case 1:
                        return call_user_func_array(array($this, 'funcOneWithOneArg'), $args);
                    case 3:
                        return call_user_func_array(array($this, 'funcOneWithThreeArgs'), $args);
                 }
            case 'anotherFunc':
                switch (count($args)) {
                    case 0:
                        return $this->anotherFuncWithNoArgs();
                    case 5:
                        return call_user_func_array(array($this, 'anotherFuncWithMoreArgs'), $args);
                }
        }
    }

    protected function funcOneWithOneArg($a) {

    }

    protected function funcOneWithThreeArgs($a, $b, $c) {

    }

    protected function anotherFuncWithNoArgs() {

    }

    protected function anotherFuncWithMoreArgs($a, $b, $c, $d, $e) {

    }

}

20
Ich habe diese Verwendung noch nie gesehen __call(). Ziemlich kreativ (wenn auch etwas ausführlich)! +1
BoltClock

Wirklich bewundernswerte Verwendung von __call ()
Abhishek Gupta

2
Kann dem eigentlich nicht zustimmen und muss mit diesem Vorschlag eine Umschulung durchführen. Zum einen ist diese Verwendung von __call () ein Anti-Pattern. Zweitens ist es möglich, in PHP eine Überladung für Klassenmethoden durchzuführen, die die richtige Sichtbarkeit haben. Sie können jedoch keine einfachen Jane-Funktionen überladen.
Oddman

1
Können Sie erklären, warum die Verwendung von __call () Ihrer Meinung nach ein Anti-Pattern ist? Das Überladen von PHP-Methoden ist nicht das, wonach das OP sucht - sie möchten die Möglichkeit haben, mehrere Methodensignaturen mit demselben Namen, aber unterschiedlicher Eingabe / Ausgabe zu haben: en.wikipedia.org/wiki/Function_overloading
Stephen

20
Es ist nicht erforderlich, __call () zu verwenden. Deklarieren Sie stattdessen eine Methode mit dem gewünschten Namen, ohne dass Parameter aufgeführt sind, und verwenden Sie func_get_args () innerhalb dieser Methode, um sie an die entsprechende private Implementierung zu senden.
FantasticJamieBurns

30

Um eine Funktion zu überladen, übergeben Sie den Parameter einfach standardmäßig als null.

class ParentClass
{
   function mymethod($arg1 = null, $arg2 = null, $arg3 = null)  
     {  
        if( $arg1 == null && $arg2 == null && $arg3 == null ){ 
           return 'function has got zero parameters <br />';
        }
        else
        {
           $str = '';
           if( $arg1 != null ) 
              $str .= "arg1 = ".$arg1." <br />";

           if( $arg2 != null ) 
              $str .= "arg2 = ".$arg2." <br />";

           if( $arg3 != null ) 
              $str .= "arg3 = ".$arg3." <br />";

           return $str;
         }
     }
}

// and call it in order given below ...

 $obj = new ParentClass;

 echo '<br />$obj->mymethod()<br />';
 echo $obj->mymethod();

 echo '<br />$obj->mymethod(null,"test") <br />';
 echo $obj->mymethod(null,'test');

 echo '<br /> $obj->mymethod("test","test","test")<br />';
 echo $obj->mymethod('test','test','test');

4
Ich betrachte Standardparameter nicht als Funktionsüberladung. Das Überladen von Funktionen hat mehr mit dem Aufrufen einer anderen Implementierung zu tun, die auf der Art des übergebenen Arguments basiert. Wenn Sie Standardparameter verwenden, können Sie dieselbe Implementierung nur mit weniger Parametern aufrufen.
Skalierbar

1
Ja, Sie können es auch typbasiert manipulieren, aber als ob Sie die lose typisierte PHP-Sprache kennen und damit umgehen müssen, müssen Sie dies angehen.
Adil Abbasi

1
Ich ziehe diese Antwort der akzeptierten vor, da sie explizit die minimale und maximale Anzahl von Parametern angibt. (Geben Sie keinen Standardwert für die erforderlichen Parameter an.) @Scalable - Ich stimme Adil zu, dass dies, da PHP lose eingegeben ist, overloadpraktisch alles ist, was es in PHP für eine Funktion bedeuten kann - trotzdem machen Sie einen nützlichen Punkt dass die Leser wissen sollten.
ToolmakerSteve

11

Für manche mag es hackisch sein, aber ich habe auf diese Weise gelernt, wie Cakephp einige Funktionen ausführt, und es angepasst, weil mir die Flexibilität gefällt, die es schafft

Die Idee ist, dass Sie verschiedene Arten von Argumenten, Arrays, Objekten usw. haben. Dann erkennen Sie, was Ihnen übergeben wurde, und gehen von dort aus

function($arg1, $lastname) {
    if(is_array($arg1)){
        $lastname = $arg1['lastname'];
        $firstname = $arg1['firstname'];
    } else {
        $firstname = $arg1;
    }
    ...
}

1
Nein, ich sehe das nicht als hackisch an, PHP macht das für viele seiner eingebauten Funktionen.
BoltClock

Da PHP lose geschrieben ist, muss man genau so mit dieser Situation umgehen. Seine "notwendige Hackishness" in PHP.
ToolmakerSteve

11
<?php   
/*******************************
 * author  : hishamdalal@gmail.com 
 * version : 3.8
 * create on : 2017-09-17
 * updated on : 2020-01-12
 *****************************/

#> 1. Include Overloadable class

class Overloadable
{
    static function call($obj, $method, $params=null) {
        $class = get_class($obj);
        // Get real method name
        $suffix_method_name = $method.self::getMethodSuffix($method, $params);

        if (method_exists($obj, $suffix_method_name)) {
            // Call method
            return call_user_func_array(array($obj, $suffix_method_name), $params);
        }else{
            throw new Exception('Tried to call unknown method '.$class.'::'.$suffix_method_name);
        }
    }

    static function getMethodSuffix($method, $params_ary=array()) {
        $c = '__';
        if(is_array($params_ary)){
            foreach($params_ary as $i=>$param){
                // Adding special characters to the end of method name 
                switch(gettype($param)){
                    case 'array':       $c .= 'a'; break;
                    case 'boolean':     $c .= 'b'; break;
                    case 'double':      $c .= 'd'; break;
                    case 'integer':     $c .= 'i'; break;
                    case 'NULL':        $c .= 'n'; break;
                    case 'object':
                        // Support closure parameter
                        if($param instanceof Closure ){
                            $c .= 'c';
                        }else{
                            $c .= 'o'; 
                        }
                    break;
                    case 'resource':    $c .= 'r'; break;
                    case 'string':      $c .= 's'; break;
                    case 'unknown type':$c .= 'u'; break;
                }
            }
        }
        return $c;
    }
    // Get a reference variable by name
    static function &refAccess($var_name) {
        $r =& $GLOBALS["$var_name"]; 
        return $r;
    }
}
//----------------------------------------------------------
#> 2. create new class
//----------------------------------------------------------

class test 
{
    private $name = 'test-1';

    #> 3. Add __call 'magic method' to your class

    // Call Overloadable class 
    // you must copy this method in your class to activate overloading
    function __call($method, $args) {
        return Overloadable::call($this, $method, $args);
    }

    #> 4. Add your methods with __ and arg type as one letter ie:(__i, __s, __is) and so on.
    #> methodname__i = methodname($integer)
    #> methodname__s = methodname($string)
    #> methodname__is = methodname($integer, $string)

    // func(void)
    function func__() {
        pre('func(void)', __function__);
    }
    // func(integer)
    function func__i($int) {
        pre('func(integer '.$int.')', __function__);
    }
    // func(string)
    function func__s($string) {
        pre('func(string '.$string.')', __function__);
    }    
    // func(string, object)
    function func__so($string, $object) {
        pre('func(string '.$string.', '.print_r($object, 1).')', __function__);
        //pre($object, 'Object: ');
    }
    // func(closure)
    function func__c(Closure $callback) {

        pre("func(".
            print_r(
                array( $callback, $callback($this->name) ), 
                1
            ).");", __function__.'(Closure)'
        );

    }   
    // anotherFunction(array)
    function anotherFunction__a($array) {
        pre('anotherFunction('.print_r($array, 1).')', __function__);
        $array[0]++;        // change the reference value
        $array['val']++;    // change the reference value
    }
    // anotherFunction(string)
    function anotherFunction__s($key) {
        pre('anotherFunction(string '.$key.')', __function__);
        // Get a reference
        $a2 =& Overloadable::refAccess($key); // $a2 =& $GLOBALS['val'];
        $a2 *= 3;   // change the reference value
    }

}

//----------------------------------------------------------
// Some data to work with:
$val  = 10;
class obj {
    private $x=10;
}

//----------------------------------------------------------
#> 5. create your object

// Start
$t = new test;

#> 6. Call your method

// Call first method with no args:
$t->func(); 
// Output: func(void)

$t->func($val);
// Output: func(integer 10)

$t->func("hello");
// Output: func(string hello)

$t->func("str", new obj());
/* Output: 
func(string str, obj Object
(
    [x:obj:private] => 10
)
)
*/

// call method with closure function
$t->func(function($n){
    return strtoupper($n);
});

/* Output:
func(Array
(
    [0] => Closure Object
        (
            [parameter] => Array
                (
                    [$n] => 
                )

        )

    [1] => TEST-1
)
);
*/

## Passing by Reference:

echo '<br><br>$val='.$val;
// Output: $val=10

$t->anotherFunction(array(&$val, 'val'=>&$val));
/* Output:
anotherFunction(Array
(
    [0] => 10
    [val] => 10
)
)
*/

echo 'Result: $val='.$val;
// Output: $val=12

$t->anotherFunction('val');
// Output: anotherFunction(string val)

echo 'Result: $val='.$val;
// Output: $val=36







// Helper function
//----------------------------------------------------------
function pre($mixed, $title=null){
    $output = "<fieldset>";
    $output .= $title ? "<legend><h2>$title</h2></legend>" : "";
    $output .= '<pre>'. print_r($mixed, 1). '</pre>';
    $output .= "</fieldset>";
    echo $output;
}
//----------------------------------------------------------

4
Können Sie eine Erklärung zur Verwendung dieser Klasse hinzufügen?
Justus Romijn

1- neue Klasse erstellen 2- erweitert überladbar. 3- Erstellen Sie Funktionen wie funcname_ () => keine Argumente oder wie funcname_s ($ s) => Zeichenfolge arg </ li>
Hisham Dalal

1
Das ist eine ziemlich coole Lösung. Warum machst du $ o = new $ obj ()? Ich habe es noch nicht ausprobiert, obwohl ich denke, es sollte \ $ o = \ $ das sein?
over_optimistic

Vielen Dank für diesen wichtigen Hinweis, und ich werde Backslash verwenden, aber es funktioniert mit Backslash und ohne! - Ich benutze phpEazy als lokalen Server.
Hisham Dalal

4

Was ist damit:

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}

Könnte funktionieren, ist aber weniger lesbar, wenn die Überladung unterschiedliche Parameter mit unterschiedlichen Namen und Bedeutungen hat.
Mathias Lykkegaard Lorenzen

3

In 5.6 PHP können Sie die Verwendung Splat - Operator ... als letzter Parameter und abschaffen func_get_args()und func_num_args():

function example(...$args)
{
   count($args); // Equivalent to func_num_args()
}

example(1, 2);
example(1, 2, 3, 4, 5, 6, 7);

Sie können damit auch Argumente entpacken:

$args[] = 1;
$args[] = 2;
$args[] = 3;
example(...$args);

Ist äquivalent zu:

example(1, 2, 3);

1
<?php

    class abs
    {
        public function volume($arg1=null, $arg2=null, $arg3=null)
        {   
            if($arg1 == null && $arg2 == null && $arg3 == null)
        {
            echo "function has no arguments. <br>";
        }

        else if($arg1 != null && $arg2 != null && $arg3 != null)
            {
            $volume=$arg1*$arg2*$arg3;
            echo "volume of a cuboid ".$volume ."<br>";
            }
            else if($arg1 != null && $arg2 != null)
            {
            $area=$arg1*$arg2;
            echo "area of square  = " .$area ."<br>";
            }
            else if($arg1 != null)
            {
            $volume=$arg1*$arg1*$arg1; 
            echo "volume of a cube = ".$volume ."<br>";
            }


        }


    }

    $obj=new abs();
    echo "For no arguments. <br>";
    $obj->volume();
    echo "For one arguments. <br>";
    $obj->volume(3);
    echo "For two arguments. <br>";
    $obj->volume(3,4);
    echo "For three arguments. <br>";
    $obj->volume(3,4,5);
    ?>

Versuchen Sie, die Frage zu bearbeiten und die Formatierung zu verwenden. Dadurch wird Ihre Antwort lesbarer und es werden mehr Benutzer angezogen.
Kashish Arora



0

Leider gibt es in PHP keine Überlastung wie in C #. Aber ich habe einen kleinen Trick. Ich deklariere Argumente mit Standard-Nullwerten und überprüfe sie in einer Funktion. Auf diese Weise kann meine Funktion abhängig von den Argumenten verschiedene Dinge tun. Unten ist ein einfaches Beispiel:

public function query($queryString, $class = null) //second arg. is optional
{
    $query = $this->dbLink->prepare($queryString);
    $query->execute();

    //if there is second argument method does different thing
    if (!is_null($class)) { 
        $query->setFetchMode(PDO::FETCH_CLASS, $class);
    }

    return $query->fetchAll();
}

//This loads rows in to array of class
$Result = $this->query($queryString, "SomeClass");
//This loads rows as standard arrays
$Result = $this->query($queryString);

1
Bitte lesen Sie alle vorhandenen Antworten durch, bevor Sie ein Jahr später eine neue schreiben. Diese Technik wurde bereits zweimal in den obigen Antworten gezeigt. Einmal im Jahr 2013 und erneut im Jahr 2014.
ToolmakerSteve
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.