PHP function overloading
Asked Answered
K

9

235

Coming from C++ background ;)
How can I overload PHP functions?

One function definition if there are any arguments, and another if there are no arguments? Is it possible in PHP? Or should I use if else to check if there are any parameters passed from $_GET and POST?? and relate them?

Kilan answered 15/1, 2011 at 3:3 Comment(7)
You can only overload class methods, but not functions. See php.net/manual/en/language.oop5.overloading.phpMessroom
You can create a function that checks the number of arguments explicitly and executes another function, from a predefined set of them. Hoever you'd better re-design your solution, or use classes that implement your interfaceOosperm
As the php.net/manual/en/language.oop5.overloading.php says, PHP's definition of overloading is different than the typical OOP language. They just refer to magic methods that allow for dynamic routing of properties and functions based on X.Geochemistry
For future readers: What @Messroom is referring to, is a different meaning for the word overloading, than is being asked in the question. (See the accepted answer for more details.)Juliennejuliet
Anything changed since PHP 7? :oUlmaceous
@Ulmaceous even earlier then PHP 7 in PHP 5.6 they added variadic functions https://mcmap.net/q/117088/-php-function-overloadingBristling
@Bristling while that may answer op's question, variadic function is not strictly function overloading.Ulmaceous
N
251

You cannot overload PHP functions. Function signatures are based only on their names and do not include argument lists, so you cannot have two functions with the same name. Class method overloading is different in PHP than in many other languages. PHP uses the same word but it describes a different pattern.

You can, however, declare a variadic function that takes in a variable number of arguments. You would use func_num_args() and func_get_arg() to get the arguments passed, and use them normally.

For example:

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);
Noble answered 15/1, 2011 at 3:4 Comment(4)
Maybe I've been doing C++ development too much, but I would suggest a hint that this is being done in the function parameters like myFunc(/*...*/).Lite
@doug65536, PHP 5.6+ will support that "..." as a syntax token, for our great relief. ;)Semirigid
Or see Adil's answer, which is closer to C++'s overloading - as close as it is possible to get, in a loosely typed language like php. Its even more appropriate in php 7, as you can provide type hints for parameters, if they are the same type in all your overloads.Juliennejuliet
Using variadic function is fine as long as it's not overused and abused (used only when it's called for) but emulating that C++ behaviour is bad. Because it always involves checking for condition with either switch case or if/else and that induces overhead. That might not be a stellar cost to performance but when a function is called often, it adds up. And quite frankly, as small as that performance cost is, I am not convinced that it is ever a more acceptable solution than just using several method names.Werby
C
91

PHP doesn't support traditional method overloading, however one way you might be able to achieve what you want, would be to make use of the __call magic method:

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

    }

}
Cultivable answered 15/1, 2011 at 3:25 Comment(6)
I've not seen this use of __call() before. Pretty creative (if a little verbose)! +1Noble
Actually, can't agree with this, and must exercise retraint with this suggestion. For one, this use of __call() is an anti-pattern. Secondly, it IS possible to do overloading in PHP for class methods that have the correct visibility. You cannot, however - overload plain-jane functions.Hypaethral
Can you explain why you think using __call() is an anti-pattern? PHP Method overloading is not what the OP is looking for - they want the ability to have multiple method signatures with the same name but different input/output: en.wikipedia.org/wiki/Function_overloadingCultivable
There is no need to use __call(). Instead declare a method having the name you desire, without any parameters listed, and use func_get_args() within that method to dispatch to the appropriate private implementation.Carolynecarolynn
That is more or less the recomendation of the manual, but the __call() and __callStatic() have a little problem: they can't receive parameters by reference, so i would have to implement some methods overloading in a way, and others, in other way, or stop passing by reference, and i think bad of both.Slavin
For future readers: What @Hypaethral is referring to when he says "it IS possible to do overloading ...", is a different meaning for the word "overloading", than is being asked in the question. (See the accepted answer for more details.)Juliennejuliet
M
31

To over load a function simply do pass parameter as null by default,

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');
Metralgia answered 11/6, 2013 at 10:10 Comment(4)
I don't consider default parameter as function overloading. function [or method] overloading has more to do with calling a different implementation based on the type of argument passed. Using default parameters only allows you to call the same implementation with the convenience of fewer parameters.Amero
yes, you can manipulate it on type based as well but as if you know php loosely typed language and dealing with it requires to tackle that.Metralgia
I prefer this answer to the accepted one, as it makes it explicit what the minimum and maximum number of parameters should be. (Don't provide a default value for parameters that are required.) @Amero - I agree with Adil that, since php is loosely typed, this is effectively all it can mean in php to overload a function - neverlheless, you make a useful point that readers should be aware of.Juliennejuliet
That has nothing to do with topic. The point of traditional method overloading is to allow functions that are allowed to have the same name provided they have different argument count and / or argument types. And enforcing the number or arguments goes against that. But you are right in that as such there is nothing enforcing that client uses method properly. For several reasons I think emulating that behaviour in PHP is a bad idea and methods with different names should be used.Werby
U
12

It may be hackish to some, but I learned this way from how Cakephp does some functions and have adapted it because I like the flexibility it creates

The idea is you have different type of arguments, arrays, objects etc, then you detect what you were passed and go from there

function($arg1, $lastname) {
    if(is_array($arg1)){
        $lastname = $arg1['lastname'];
        $firstname = $arg1['firstname'];
    } else {
        $firstname = $arg1;
    }
    ...
}
Uncommitted answered 15/1, 2011 at 4:29 Comment(2)
Nah I don't see this as hackish, PHP does this for many of its built-in functions.Noble
Because php is loosely typed, this is exactly how one must handle this situation. Its "necessary hackishness" in php.Juliennejuliet
D
11
<?php   
// download example:  https://github.com/hishamdalal/overloadable 

#> 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;
}
//----------------------------------------------------------
Dara answered 11/4, 2013 at 10:44 Comment(4)
Could you add some explanation how to use this class?Gallup
1- create new class 2- extends overloadable. 3- create functions like funcname_() => no args or like funcname_s($s) => string arg</li>Dara
This is pretty cool solution. Why do you do $o = new $obj()? I didn't try it out yet, though I think it should be \$o = \$this?Jidda
Thank you for this important notice, and I will use backslash, but it's work with backslash and without! - I use phpEazy as a local server.Dara
B
6

In PHP 5.6 you can use the splat operator ... as the last parameter and do away with func_get_args() and func_num_args():

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

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

You can use it to unpack arguments as well:

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

Is equivalent to:

example(1, 2, 3);
Bristling answered 5/4, 2018 at 2:29 Comment(0)
S
4

What about this:

function($arg = NULL) {

    if ($arg != NULL) {
        etc.
        etc.
    }
}
Sheepfold answered 29/6, 2012 at 2:20 Comment(1)
Could work, but is less readable if the overload will have different parameters with different names, and meanings.Ulick
A
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);
    ?>
Atal answered 23/8, 2014 at 7:11 Comment(2)
Try to edit the question and use formatting. It will make your answer more readable and attract more users.Barocchio
This technique is shown in an earlier answer.Juliennejuliet
B
0

Sadly there is no overload in PHP as it is done in C#. But i have a little trick. I declare arguments with default null values and check them in a function. That way my function can do different things depending on arguments. Below is simple example:

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);
Brindled answered 9/2, 2018 at 19:50 Comment(1)
Please read through all existing answers, before writing a new one years later. This technique was already shown twice in answers above. Once in 2013, and again in 2014.Juliennejuliet

© 2022 - 2024 — McMap. All rights reserved.