Method Overloading in PHP5

Although with release of PHP5 we finaly got some long awaited OOP features, sometimes I really miss overloading capability which exists in languages like Java. I am talking about something like this:

class Overloading_Test
{
  public void hello()
  {
    System.out.println("Hello Anonymous");
  }

  public void hello(String name)
  {
    System.out.println("Hello " + name);
  }


  public void hello(String firstName, String lastName)
  {
    System.out.println("Hello " + firstName + " " + lastName);
  }
}

This way you can call either hello with no arguments at all, or with one or two arguments, and proper method would always be called. Unfortunately, if you try something like this in PHP, it would give you fatal error, because basically, methods cannot be redeclared, since support for overloading is not part of core language like in Java.

However, there is still a way to achieve this Java like overloading functionality by using “magic” methods that are described in PHP Manual. Although it is not clear from manual how could you achieve exact functionality like in Java, I played a little bit with __call function, and get interesting workaround.

<?php

class Overloading_Test
{
  function __call($method_name, $arguments)
  {
    //list of supported methods
    //only 'hello' for this test
    $accepted_methods = array("hello");

    //in case of unexistant method we trigger fatal error
    if(!in_array($method_name, $accepted_methods))
    {
      trigger_error("Method <strong>$method_name</strong> doesn't exist", E_USER_ERROR);
    }

    //we inspect number of arguments
    if(count($arguments) == 0)
    {
      $this->hello1();
    }
    elseif(count($arguments) == 1)
    {
      $this->hello2($arguments[0]);
    }
    elseif(count($arguments) == 2)
    {
      $this->hello3($arguments[0], $arguments[1]);
    }
    else
    {
      return false;
    }
  }

  function hello1()
  {
    echo "Hello Anonymous<br>";
  }

  function hello2($name)
  {
    echo "Hello $name<br>";
  }

  function hello3($first_name, $last_name)
  {
    echo "Hello $first_name, $last_name<br>";
  }
}


$ot = new Overloading_Test();
$ot->hello();
$ot->hello("John");
$ot->hello("John", "Smith");
//this one will produce fatal error
//$ot->test();
?>

If you run this code, you will get something like:

Hello Anonymous
Hello John
Hello John, Smith

So, what is going on here? Whenever we call some undeclared method (which is the case with ‘hello’ method here), magic method __call is called, and two arguments (method name and arguments) are passed to it. For this simple test, we only support overloading of ‘hello’ method, so in case you try any other, we trigger fatal error.

What’s going on further is, we simple check number of argumens passed (by counting $arguments array), and call proper method. For the sake of clarity, I only used simple overloading based on number of arguments, but you could also check for argument type (ie string, integer etc.) and call proper method.

So, as you see, method overloading in PHP5 is not as elegant as in Java, but you can still make it. For more information about ‘magic’ fucntions (there are quite a few for member overloading as well), please visit PHP Manual.

Comments

  1. August 2nd, 2007 | 1:03 am

    You could just do something like this for simplicity –

    class SomeClass {
    public static function hello( $firstName = false, $lastName = false ) {
    if ( $firstName === false && $lastName === false ) {
    print(“Hello Anonymous”);
    } else if ( $lastName === false ) {
    print(“Hello $firstName”);
    } else {
    print(“Hello $firstName $lastName”);
    }
    }
    }

    SomeClass::hello();
    SomeClass::hello( “Beef” );
    SomeClass::hello( “Mister”, “Beefie” );

  2. August 2nd, 2007 | 1:55 am

    Yeah, of corse I could, but then, that wouldn’t be overloading at all, but using default argument values :) Not to mention that you can’t really call the class staticly that way in php 5(SomeClass::hello()) unless class is declared as static, but I guess you just pointed to default arguments solution instead of overloading, without paying attention to that.

    Anyway, the whole point of this post is, you can do something simmilar as with my first Java example as easy as you can with Java (at least outside of class).

  3. August 2nd, 2007 | 6:42 am

    My main point was that your solution, while it may work, seems more like a workaround than a solution. Granted, my solution is not overloading either, however, to the calling code, it acts the same and is less coding and doesn’t rely on having a string value be correctly typed *G*.. I know as a developer, when I am in code cranking mode I mistype things all the time and since I do use dynamic methods in PHP (see my blog on dynamic getters/setters — Mmm… Beans for PHP) — I know all about using __call() and the power it offers.

    To reiterate, I am not saying that your solution is wrong, just that it is not the only way to achieve the effect…

    And for the record, the class does not have to be declared static to reference static properties or functions on that class.. *G* I do it all the time.

    -cheers-

  4. August 2nd, 2007 | 10:27 am

    ok I understand your point, and completely agree that this is more like workaround (or hack) than real solution. Also it could be I picked bad examples with these ‘hello’ stuff since you can achieve the same functionality in less code by using default arguments.

    About static … my bad. You *can* call it all the time, although calling non static methods statically produce E_STRICT level warning. Not a big deal, but it is something I am trying to avoid. :)

  5. November 1st, 2007 | 11:55 am

    Very good. I understand how overloading in PHP work from your article. Thanks a lot :)

  6. November 5th, 2007 | 5:32 pm

    [...] read more | digg story [...]

  7. Polad
    March 27th, 2008 | 9:29 am

    How would you do it if you’d wanna call function with the same name and same number of arguments but different types of arguments like this:

    MyClass::hello(Word $obj)
    MyClass::hello(ExtWord $obj)

    this would be a real overloading.

  8. July 7th, 2008 | 8:47 pm

    Dinke,

    Please clean up blog entries. I posted some PHP code but it didn’t escape the content so greater than and less than signs for example are rendering the code for Polad incorrectly. Also, the blog posts an entry *before* you enter the captcha which defeats the purpose of having a captcha. Consequently, there are 2 almost identical blog posts– second one is correct.

    Okay here is the code again….

    class Word {
    	// ...
    }
    
    class ExtWord extends Word {
    	// ...
    }
    
    class MyClass {
    
    	// Method Signature Overloading (Polymorphism)
    	function __call($method, $parameters) {
    		foreach (get_class_methods(__CLASS__) as $class_method) {
    			if (preg_match('/'.$method.'\d+/', $class_method)) {
    				$reflect_method = new ReflectionMethod(__CLASS__, $class_method);
    				if (count($parameters) == $reflect_method->getNumberOfParameters()) {
    					$class_parameters = $reflect_method->getParameters();
    					for ($p=0; $p<count($parameters); $p++) {
    						if (get_class($parameters[$p]) == $class_parameters[$p]->getClass()->getName()) {
    							if ($p == count($parameters)-1) {
    								call_user_func_array(array(&$this, $class_method), $parameters);
    								break 2; // Method Signature Match
    							}
    						} else {
    							break;
    						}
    					}
    				}
    			}
    		}
    	}
    
    	function hello2(Word $word) {
    		// ...
    	}
    
    	function hello3(ExtWord $extWord) {
    		// ...
    	}
    }
    
    
    $myClass = new MyClass();
    
    $myClass->hello(new Word());
    $myClass->hello(new ExtWord());
    

    Polad, if you still cannot copy the code correctly then shoot me an email at michaelnino.com and I’d be glad to reply with code or chat!

    Mike :-)

  9. January 30th, 2009 | 11:57 am

    Hello, I am learning programming in PHP these days. This easy to understand code helped in learning method overloading in PHP. So thanks a lot.

  10. Forsyth
    January 31st, 2009 | 4:38 pm

    Unfortunate problem, especially with constructors.

    The magic methods make me feel a bit queasy.

    One solution that dawned on me this morning is to keep a single method but add booleans to dictate the “type”.

    For example, my original constructor:

    class MSSSimpleXML {
    function MSSSimpleXML($data, $isURL = FALSE) {

    }
    }

    The purpose of this class is to wrap a SimpleXMLElement object. The constructor allowed you to say whether $data was a URL or an XML string.

    Now I wanted a constructor that would allow you to construct a MSSSimpleXML object from a php SimpleXMLElement.

    Constructor overloading would have been perfect but instead settled on this:

    function MSSSimpleXML($data, $isURL = FALSE, $isSimpleXML = FALSE)

    The constructor now has three modes:

    1. Build an object from an XML string ($data=[the string], $isURL=FALSE, $isSimpleXML=FALSE)

    2. Build an object from a URL that points to XML ($data=[the URL], $isURL=TRUE, $isSimpleXML=FALSE)

    3. Build an object from a SimpleXMLElement object ($data=[the SimpleXMLElement instance, $isURL=FALSE, $isSimpleXML=TRUE)

    Not terribly beautiful. (It would have to be late and I’d have to be drunk to…)

    But…very simple — which is what PHP is all about.

    peace,
    -Forsyth

  11. Forsyth
    January 31st, 2009 | 4:55 pm

    Now that I look at it I believe I’ll change it to create a single “mode” flag that takes constants:

    class MSSSimpleXML {
    const URL = 1;
    const STRING = 2;
    const SIMPLEXML = 3;

    function MSSSimpleXML( $xml, $mode ) {

    }
    }

    Usage:


    new MSSSimpleXML( $xml, MSSSimpleXML::STRING );

    Would be cleaner I think.

  12. Forsyth
    January 31st, 2009 | 5:29 pm

    Final result:

    class MSSSimpleXML {

    const URL = 1;
    const STRING = 2;
    const SIMPLEXML = 3;

    function MSSSimpleXML($data, $mode=self::STRING ) {

    switch ($mode)
    {
    case self::STRING:

    break;

    case self::URL:

    break;

    case self::SIMPLEXML:

    break;
    }
    }

    }

    Makes default constructor assume STRING.

    Usage:

    // constructor where data is xml in string form
    $xml = new MSSSimpleXML( $data );

    // same
    $xml = new MSSSimpleXML( $data, MSSSimpleXML::STRING );

    // constructor where data is url pointing to xml
    $xml = new MSSSimpleXML( $data, MSSSimpleXML::URL );

    // constructor where data is SimpleXMLElement object
    $xml = new MSSSimpleXML( $data, MSSSimpleXML::SIMPLEXML );

    If you had additional arguments that varied by mode you could add an associative array of arguments…

    peace,
    -Forsyth

  13. Deecee
    July 21st, 2009 | 7:36 pm

    This is really good example of Overloading in PHP, specially given example with Java.

    Thanks a Lot.

  14. May 15th, 2010 | 8:43 pm

    Hello
    I have shown a good example in my blog ( http://blog.actcode.com/2010/05/php-overloading-method-overloading-and.html ).
    It’s clearly described there , that how overloading should implement.

    Thnaks
    Tanvir.

  15. November 20th, 2010 | 11:49 am

    [...] metod jest użycie magicznych metod. Sposób takiej implementacji można podejrzeć w wpisie “Method Overloading in PHP5″ na blogu Caught in a Web. Osobiście nie przepadam za tego rodzaju rozwiązaniami. Tak więc w [...]

  16. January 14th, 2011 | 4:57 pm

    Can I use both PHP4 and PHP5 on the same dedicated server?

  17. June 25th, 2011 | 2:16 pm

    nice

Leave a reply