Sovraccarico della funzione PHP


196

Proveniente dallo sfondo C ++;)
Come posso sovraccaricare le funzioni PHP?

Una definizione di funzione se ci sono argomenti e un'altra se non ci sono argomenti? È possibile in PHP? O dovrei usare if else per verificare se ci sono parametri passati da $ _GET e POST ?? e metterli in relazione?


1
Puoi solo sovraccaricare i metodi di classe, ma non le funzioni. Vedere php.net/manual/en/language.oop5.overloading.php
Spechal

1
È possibile creare una funzione che controlla esplicitamente il numero di argomenti ed esegue un'altra funzione, da un set predefinito di essi. Tuttavia, è meglio riprogettare la soluzione o utilizzare le classi che implementano la tua interfaccia
kolypto

2
Come dice php.net/manual/en/language.oop5.overloading.php , la definizione di sovraccarico di PHP è diversa dal tipico linguaggio OOP. Si riferiscono solo a metodi magici che consentono il routing dinamico di proprietà e funzioni basate su X.
Edwin Daniels,

Per i futuri lettori: ciò a cui si riferisce @Spechal è un significato diverso per la parola overloadingrispetto a quello che viene posto nella domanda. (Vedi la risposta accettata per maggiori dettagli.)
ToolmakerSteve

2
Qualcosa è cambiato da PHP 7? : o
nawfal,

Risposte:


219

Non è possibile sovraccaricare le funzioni PHP. Le firme delle funzioni si basano solo sui loro nomi e non includono elenchi di argomenti, quindi non è possibile avere due funzioni con lo stesso nome. Il sovraccarico del metodo di classe è diverso in PHP che in molte altre lingue. PHP usa la stessa parola ma descrive uno schema diverso.

Tuttavia, è possibile dichiarare una funzione variadica che accetta un numero variabile di argomenti. Dovresti usare func_num_args()e func_get_arg()per far passare gli argomenti e usarli normalmente.

Per esempio:

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
Forse ho fatto troppo sviluppo C ++, ma suggerirei un suggerimento che questo è stato fatto nei parametri di funzione come myFunc(/*...*/).
doug65536

4
@ doug65536, PHP 5.6+ supporterà quel "..." come token di sintassi , per il nostro grande sollievo. ;)
Sz.

Oppure vedi la risposta di Adil , che è più vicina al sovraccarico di C ++ - il più vicino possibile, in un linguaggio vagamente tipizzato come php. È ancora più appropriato in php 7, poiché puoi fornire suggerimenti sul tipo per i parametri, se sono dello stesso tipo in tutti i tuoi sovraccarichi.
ToolmakerSteve

78

PHP non supporta il sovraccarico del metodo tradizionale, tuttavia un modo in cui potresti essere in grado di ottenere ciò che desideri sarebbe utilizzare il __callmetodo magico:

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
Non ho mai visto questo uso __call()prima. Abbastanza creativo (se un po 'prolisso)! +1
BoltClock

Uso davvero ammirevole di __call ()
Abhishek Gupta

2
In realtà, non posso essere d'accordo con questo e deve esercitare un nuovo controllo con questo suggerimento. Per uno, questo uso di __call () è un anti-pattern. In secondo luogo, è possibile fare un sovraccarico in PHP per i metodi di classe che hanno la visibilità corretta. Tuttavia, non è possibile sovraccaricare le funzioni di jane normale.
Oddman,

1
Puoi spiegare perché pensi che usare __call () sia un anti-pattern? Il sovraccarico del metodo PHP non è ciò che l'OP sta cercando: vogliono la possibilità di avere più firme del metodo con lo stesso nome ma input / output diversi: en.wikipedia.org/wiki/Function_overloading
Stephen,

20
Non è necessario utilizzare __call (). Dichiarare invece un metodo con il nome desiderato, senza alcun parametro elencato, e utilizzare func_get_args () all'interno di quel metodo per inviare l'implementazione privata appropriata.
FantasticJamieBurns,

30

Per sovraccaricare una funzione è sufficiente passare il parametro come null per impostazione predefinita,

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
Non considero il parametro predefinito come sovraccarico di funzioni. Il sovraccarico della funzione [o metodo] ha più a che fare con la chiamata di un'implementazione diversa in base al tipo di argomento passato. L'uso dei parametri predefiniti consente di chiamare la stessa implementazione solo con la comodità di un minor numero di parametri.
Scalabile

1
sì, è possibile manipolarlo anche in base al tipo, ma come se si conoscesse un linguaggio tipicamente diffuso php e affrontarlo richiede di affrontarlo.
Adil Abbasi,

1
Preferisco questa risposta a quella accettata, in quanto rende esplicito quale dovrebbe essere il numero minimo e massimo di parametri. (Non fornire un valore predefinito per i parametri richiesti.) @Scalable - Sono d'accordo con Adil che, dato che php è tipicamente impresso, questo è effettivamente tutto ciò che può significare in php per overloaduna funzione - tuttavia, fai un punto utile di cui i lettori dovrebbero essere a conoscenza.
ToolmakerSteve

11

Può essere un po 'di hacking per alcuni, ma ho imparato in questo modo da come Cakephp svolge alcune funzioni e l'ho adattato perché mi piace la flessibilità che crea

L'idea è che hai diversi tipi di argomenti, matrici, oggetti ecc., Quindi rilevi ciò che ti è stato passato e vai da lì

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

1
No, non lo vedo come un hacker, PHP lo fa per molte delle sue funzioni integrate.
BoltClock

Poiché php è tipizzato in modo approssimativo, questo è esattamente come si deve gestire questa situazione. La sua "necessaria 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
Potresti aggiungere qualche spiegazione su come usare questa classe?
Justus Romijn l'

1- crea una nuova classe 2- estende il sovraccarico. 3- creare funzioni come funcname_ () => no args o like funcname_s ($ s) => string arg </li>
Hisham Dalal

1
Questa è una soluzione davvero interessante. Perché fai $ o = new $ obj ()? Non l'ho ancora provato, anche se penso che dovrebbe essere \ $ o = \ $ questo?
over_optimistic

Grazie per questo importante avviso e userò la barra rovesciata, ma funziona con la barra rovesciata e senza! - Uso phpEazy come server locale.
Hisham Dalal,


3

In PHP 5.6 puoi usare l' operatore splat ... come ultimo parametro e eliminare func_get_args()e func_num_args():

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

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

Puoi usarlo anche per decomprimere gli argomenti:

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

È equivalente a:

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);
    ?>

Prova a modificare la domanda e usa la formattazione. Renderà la tua risposta più leggibile e attirerà più utenti.
Kashish Arora,


0

PHP non supporta il sovraccarico per ora. Spero che questo sia implementato nelle altre versioni come altri linguaggi di programmazione.

Dai un'occhiata a questa libreria, questo ti permetterà di utilizzare il sovraccarico PHP in termini di chiusure. https://github.com/Sahil-Gulati/Overloading


1
se hai intenzione di fare una dichiarazione come questa, dovresti davvero includere le versioni a cui ti riferisci, in questo modo le persone sanno quanto è obsoleto il tuo commento quando lo vedono in una data futura
MikeT

0

Purtroppo non c'è sovraccarico in PHP come avviene in C #. Ma ho un piccolo trucco. Dichiaro argomenti con valori null predefiniti e li controllo in una funzione. In questo modo la mia funzione può fare cose diverse a seconda degli argomenti. Di seguito è riportato un semplice esempio:

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
Si prega di leggere tutte le risposte esistenti, prima di scriverne una nuova anni dopo. Questa tecnica è già stata mostrata due volte nelle risposte sopra. Una volta nel 2013, e ancora nel 2014.
ToolmakerSteve
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.