Sobrecarga de funciones PHP

195

Procedente de fondo C ++;)
¿Cómo puedo sobrecargar las funciones de PHP?

¿Una definición de función si hay argumentos y otra si no hay argumentos? ¿Es posible en PHP? ¿O debería usar if else para verificar si hay algún parámetro pasado de $ _GET y POST? y relacionarlos?

Vamsi Krishna B
fuente
1
Solo puede sobrecargar los métodos de clase, pero no las funciones. Ver php.net/manual/en/language.oop5.overloading.php
Spechal
1
Puede crear una función que verifique el número de argumentos explícitamente y ejecute otra función, a partir de un conjunto predefinido de ellos. Sin embargo, será mejor que rediseñe su solución, o use clases que implementen su interfaz
kolypto
2
Como dice php.net/manual/en/language.oop5.overloading.php , la definición de sobrecarga de PHP es diferente del lenguaje típico de OOP. Simplemente se refieren a métodos mágicos que permiten el enrutamiento dinámico de propiedades y funciones basadas en X.
Edwin Daniels
Para futuros lectores: a lo que se refiere @Spechal es a un significado diferente para la palabra overloading, de lo que se pregunta en la pregunta. (Vea la respuesta aceptada para más detalles.)
ToolmakerSteve
2
¿Ha cambiado algo desde PHP 7? : o
nawfal

Respuestas:

218

No puede sobrecargar las funciones de PHP. Las firmas de funciones se basan solo en sus nombres y no incluyen listas de argumentos, por lo que no puede tener dos funciones con el mismo nombre. La sobrecarga del método de clase es diferente en PHP que en muchos otros lenguajes. PHP usa la misma palabra pero describe un patrón diferente.

Sin embargo, puede declarar una función variadic que tome un número variable de argumentos. Usaría func_num_args()y func_get_arg()para obtener los argumentos aprobados, y usarlos normalmente.

Por ejemplo:

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);
BoltClock
fuente
8
Tal vez he estado haciendo demasiado desarrollo de C ++, pero sugeriría una pista de que esto se está haciendo en los parámetros de la función myFunc(/*...*/).
doug65536
44
@ doug65536, PHP 5.6+ admitirá ese "..." como token de sintaxis , para nuestro gran alivio. ;)
Sz.
O vea la respuesta de Adil , que está más cerca de la sobrecarga de C ++, tan cerca como sea posible, en un lenguaje poco mecanografiado como php. Es aún más apropiado en php 7, ya que puede proporcionar sugerencias de tipo para los parámetros, si son del mismo tipo en todas sus sobrecargas.
ToolmakerSteve
78

PHP no admite la sobrecarga de métodos tradicionales, sin embargo, una forma de lograr lo que desea sería utilizar el __callmétodo mágico:

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) {

    }

}
Stephen
fuente
20
No he visto este uso de __call()antes. ¡Bastante creativo (aunque un poco detallado)! +1
BoltClock
Uso realmente admirable de __call ()
Abhishek Gupta
2
En realidad, no puedo estar de acuerdo con esto, y debe ejercer retractación con esta sugerencia. Por un lado, este uso de __call () es un antipatrón. En segundo lugar, es posible hacer una sobrecarga en PHP para los métodos de clase que tienen la visibilidad correcta. Sin embargo, no puede sobrecargar las funciones simples de jane.
Oddman
1
¿Puedes explicar por qué crees que usar __call () es un antipatrón? La sobrecarga de métodos PHP no es lo que está buscando el OP: quieren la capacidad de tener múltiples firmas de métodos con el mismo nombre pero diferente entrada / salida: en.wikipedia.org/wiki/Function_overloading
Stephen
20
No hay necesidad de usar __call (). En su lugar, declare un método con el nombre que desee, sin ningún parámetro en la lista, y use func_get_args () dentro de ese método para enviarlo a la implementación privada adecuada.
FantasticJamieBurns
30

Para sobrecargar una función, simplemente pase el parámetro como nulo de forma predeterminada,

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');
Adil Abbasi
fuente
44
No considero el parámetro predeterminado como sobrecarga de funciones. la sobrecarga de la función [o método] tiene más que ver con llamar a una implementación diferente en función del tipo de argumento pasado. El uso de parámetros predeterminados solo le permite llamar a la misma implementación con la comodidad de menos parámetros.
Escalable el
1
Sí, también puede manipularlo en función del tipo, pero como si conociera el lenguaje php tipeado y tratar con él requiere abordar eso.
Adil Abbasi
1
Prefiero esta respuesta a la aceptada, ya que hace explícito cuál debería ser el número mínimo y máximo de parámetros. (No proporcione un valor predeterminado para los parámetros que se requieren). @Scalable: estoy de acuerdo con Adil en que, dado que php se escribe libremente, esto es efectivamente todo lo que puede significar en php para overloaduna función; sin embargo, usted hace un punto útil que los lectores deben tener en cuenta.
ToolmakerSteve
11

Puede ser difícil para algunos, pero aprendí de esta manera por cómo Cakephp realiza algunas funciones y lo he adaptado porque me gusta la flexibilidad que crea

La idea es que tenga diferentes tipos de argumentos, matrices, objetos, etc., luego detecte lo que le pasó y vaya desde allí

function($arg1, $lastname) {
    if(is_array($arg1)){
        $lastname = $arg1['lastname'];
        $firstname = $arg1['firstname'];
    } else {
        $firstname = $arg1;
    }
    ...
}
SeanDowney
fuente
1
No, no veo esto como hack, PHP lo hace para muchas de sus funciones integradas.
BoltClock
Debido a que php está mal escrito, así es exactamente como uno debe manejar esta situación. Su "hackishness necesario" en php.
ToolmakerSteve
11
<?php   
/*******************************
 * author  : [email protected] 
 * 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;
}
//----------------------------------------------------------
Hisham Dalal
fuente
44
¿Podría agregar alguna explicación sobre cómo usar esta clase?
Justus Romijn
1- crear nueva clase 2- se extiende sobrecargable. 3- crea funciones como funcname_ () => sin argumentos o como funcname_s ($ s) => string arg </li>
Hisham Dalal
1
Esta es una solución genial. ¿Por qué haces $ o = new $ obj ()? Todavía no lo probé, aunque creo que debería ser \ $ o = \ $ this?
over_optimistic
Gracias por este aviso importante, y usaré la barra invertida, ¡pero funciona con barra invertida y sin ella! - Uso phpEazy como servidor local.
Hisham Dalal
4

¿Qué hay de esto?

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}
Marlin Ouverson
fuente
Podría funcionar, pero es menos legible si la sobrecarga tendrá diferentes parámetros con diferentes nombres y significados.
Mathias Lykkegaard Lorenzen
3

En PHP 5.6 puede usar el operador splat ... como último parámetro y eliminar func_get_args()y func_num_args():

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

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

También puede usarlo para descomprimir argumentos:

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

Es equivalente a:

example(1, 2, 3);
mtpultz
fuente
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);
    ?>
Raman Deep Bajwa
fuente
Intenta editar la pregunta y usa el formato. Hará que su respuesta sea más legible y atraerá a más usuarios.
Kashish Arora
Esta técnica se muestra en una respuesta anterior .
ToolmakerSteve
0

PHP no admite sobrecarga por ahora. Espero que esto se implemente en las otras versiones, como otros lenguajes de programación.

Echa un vistazo a esta biblioteca, esto te permitirá utilizar la sobrecarga de PHP en términos de cierres. https://github.com/Sahil-Gulati/Overloading

Sahil Gulati
fuente
1
si va a hacer una declaración como esta realmente debería incluir las versiones a las que se refiere, de esa manera la gente sabrá cuán desactualizado está su comentario cuando lo vean en una fecha futura
MikeT
0

Lamentablemente, no hay sobrecarga en PHP como se hace en C #. Pero tengo un pequeño truco. Declaro argumentos con valores nulos predeterminados y los verifico en una función. De esa manera, mi función puede hacer cosas diferentes dependiendo de los argumentos. A continuación se muestra un ejemplo simple:

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);
Rovshan Mamedov
fuente
1
Lea todas las respuestas existentes antes de escribir una nueva años más tarde. Esta técnica ya se mostró dos veces en las respuestas anteriores. Una vez en 2013, y nuevamente en 2014.
ToolmakerSteve