Table of Contents

One of PHP‘s lesser known features is the

<a href="http://www.php.net/overload" target="_blank">overload extension</a>

, which with PHP 4.3.0 has become part of the core distribution.

Here we take a look at how it works and make some suggestions for how we can use to extend the life of our code.

What is Overloading

Overloading is the ability of a programming language to use the same name for more than one variable or procedure, requiring the compiler / intepreter to differentiate them based on context.

For strongly typed languages like C++ and Java, overloading is an important feature, as it allows functions / class methods to be behave differently depending on the number and type of arguments they receive, as well as being able to return different variable types.

A simple example in Java;

class Hello {
  greet() {
    System.out.println("Hello");
  }
  greet(string name) {
    System.out.println("Hello " + name);
  }
}

There are now two greet() methods. The one which is called depends on whether the client code to the Hello class passes a string argument or not.

The requirement for this functionality in PHP is not so pressing. As a loosely typed language we can have a function receive and return anything we like, perhaps with a little help from in built PHP functions like func_get_args(). We can throw as many arguments as we like at a PHP function without having to define them in the function itself. So a (more or less) equivalent PHP class to the above Java example might be;

class Hello {
    function greet() {
        $args=func_get_args();
        if (isset($args[0]) && is_string($args[0])) {
            print ('Hello '.$args[0]);
        } else {
            print("Hello");
        }
    }
}

Note that the following isn’t possible in PHP;

class Hello {
    function greet() {
        print("Hello");
    }
    function greet ($name) {
        print("Hello ".$name);
    }
}

The parser will complain about greet() being declared twice.

Note also: overloading in some languages (e.g. C++) also applies to operators (+, -, * etc.) - PHP does not support this (and that’s probably a very good thing).

Now we know what overloading is in terms of other languages, it’s time to forget all that and talk about PHP‘s overload extension.

It terms of what overloading usually means in other languages, the overload extension is perhaps only loosely related. It is intended specifically for use with PHP classes, to extend them “on the fly” (i.e. at runtime), providing us a means to do all sorts of coding “backflips” and “sommersaults” in PHP.

How it works

The overload extension defines three “magic” functions; call(), set() and get(). If we place these in a class then declare (outside the class) that it is overloaded, PHP will use the magic functions if it finds that a member variable or function does not exist. Here’s a simple example using the get() magic function;

<?php
class Test {
    // Declare a variable as normal
    var $foo='$foo <b>has</b> been declared';
 
    // Declare the magic function for getting properties
    function __get($name,&$value) {
        $value="$$name has not been declared";
        return true;
    }
}
 
// Declare the class as overloaded
overload ("Test");
 
$test= new Test;
 
// Get the value of the declared variable
echo ($test->foo."<br />");
 
// Get the values of two non existent variables
echo ($test->bar."<br />");
echo ($test->foobar."<br />");
?>

This produces the following output;

$foo has been declared
$bar has not been declared
$foobar has not been declared

That’s a simple example which demonstrates the point. Whenever the $test object found that a called property didn’t exist, it passed it to the get() method to deal with. One thing to be aware of with the overload extension is the unusual way is returns values as we saw; <code> function get($name,&$value) {

      $value="$$name has not been declared";
      return true;
  }

</code>

The value returned by the return statement should only be true or false, to tell the parser whether the operation should be regarded as having succeeded or failed. The value we’re actually interested is $value which we declare as a parameter in defining get. This may seem confusing at first but makes most sense if we consider a modified version of our earlier test; <code php> <?php class Test { Declare a variable as normal var $foo=’$foo <b>has</b> been declared’; Declare the magic function for getting properties function get($name,&$value) {

      // Define an array of names we'll accept
      $accept=array('bar');
      if ( in_array($name,$accept) ) {
          $value="$$name has not been declared";
          return true;
      } else {
          return false;
      }
  }

}

Declare the class as overloaded overload (”Test”); $test= new Test; Get the value of the declared variable echo ($test→foo.”<br />”);

Get the values of two non existent variables echo ($test→bar.”<br />”); echo ($test→foobar.”<br />”); ?> </code> Notice the changes in the call method. The output is now; <code> $foo has been declared $bar has not been declared Notice: Undefined property: foobar in test.php on line 28 </code> The syntax of the magic functions, which the PHP manual doesn’t make quite clear, is; <code> boolean get([string property_name],[mixed return_value]) boolean set([string property_name],[mixed value_to_assign]) boolean call([string method_name],[array arguments],[mixed return_value]) </code> Note that we can call these magic functions directly from outside the class, such as (using our earlier example); <code> $test→get(’bar’,$value); echo ( $value ); </code> Having said that this doesn’t seem to be the intended purpose of the overload magic functions - we probably shouldn’t use them directly from outside a class and certainly there’s nothing to be gained from doing so. The call function is the most interesting and will be the subject of the rest of this discussion. Here’s a simple example of calling a function which is declared outside the class; <code php> <?php class Test { Constructor does nothing

  function Test () {}
  function __call($method,$params,&$return) {
      $return=$method($params[0]);
      return true;
  }

}

Declare the class as overloaded overload (”Test”); Define some example function function greet ($name) {

  echo ( 'Hello '.$name );

}

$test= new Test;

Call greet() via via the $test object $test→greet(’World!’); ?> </code> There’s a few things to note here. First we remember that the overload extension refers to call() if a method is called which it can’t find declared in the local scope. This includes the class’s constructor. PHP automatically attempts to call a constructor, whether we’ve declared one or not so if no constructor exists, the overload function will attempt to use call() to do it for us. In the above example we declare a “blank” constructor for this purpose. Second the value of $method inside the call function will always be lower case. This is because PHP itself converts all function calls to lower case, to allow them to be case insensitive. I recently got my knuckles rapped with a “bogus” for reporting this as a bug.. Although for most cases, this isn’t a problem, it is something developers need to be aware of, particularily when interacting with other systems where case does matter (such as the command line or in web services (we’ll see how PEAR::SOAP uses the overload extension and overcomes this problem shortly...). The third point to be aware of is all arguments passed to the call() method are placed inside an indexed array ($params in the example). So in our example, $params looked like; <code> <?php print_r ($params); ... ?>

Array (

  [0] => World!

) </code>

Fouthly, there’s downside overloading a class (at least with the version of PHP I’m running - 4.3.0 on Windows XP) and using the call method; we can’t return values from our own methods - we have to resort to accessing the property directly. For example; <code php> <?php class Test { var $test; function Test () { $this→test=’Hello World’; } function getTest () { return $this→test; } function call () {

  }

}

Declare the class as overloaded overload (”Test”); $test=new Test; echo ( $test→getTest() ); This returns nothing echo ( $test→test ); This works. ?> </code> If we remove the declaring of call(), getTest() will correctly return values again. Is this a bug? I leave it to someone else to report this time ;) Otherwise, as before with the get() method, the return value we’re actually interested in is stored in $return, for which we use the reference operator as good practice for scenarios such as where we return an object. ======Putting the Overload Extension to Work====== To some extent, we could argue that the overload extension is a solution looking for a problem. We can live happily without it but in certain circumstances, it could provide a more versatile and intuitive solution to a problem. =====PEAR::SOAP and the SWSAPI===== Activestate have done some work in defining a unified API for web services, for loosely typed languages; the Simple Web Services API. Currently PHP, Perl and Python are on the list. For PHP they’ve built a wrapper around Shane Caraveo’s PEAR::SOAP code (the whole lot is bundled as a single downloadString API for example, in PHP we could have a class like; <code php> class String { var $string; function String ($string) { $this→string=$string; } function call($method,$params,$return) { $numargs=count($params); switch ( $numargs ) { case 1: $return=$method($this→string,$params[0]); break; case 2: $return=$method($this→string,$params[0],$params[1]); break; default; $return=$method($this→string); break; } return true; } } overload (”String”); </code> We can now call PHP functions like; <code> $string=new String(’Hello World!’); Call some PHP functions from within the $string object echo ( $string→strlen().’<br />’ ); String length echo ( $string→strtoupper().’<br />’ ); Uppercase echo ( $string→urlencode().’<br />’ ); Url encode echo ( $string→strstr(’llo’).’<br />’); Sub string </code> This displays; <code> 12 HELLO WORLD! Hello+World%21 llo World! </code> Some further work is required in the String class, which will require it has some knowledge of the of PHP‘s functions so that is builds the arguments correctly (for example str_replace places the subject string as the last argument) but in general we have a quick way to construct classes which wrap the base PHP functionality, should be need to. =====Converting Function Libraries to Class Libraries===== There’s alot of procedural PHP out there, much of it wrapped up in functions. Should we need to convert this to PHP classes, so we can begin constructing more re-usable object oriented applications, one way to save having to re-write alot of code would be to wrap the alot in an overloaded class. For example, taking a couple of functions at random from the Zend code library, we could write a script like; <code php> <?php Two legacy PHP functions function BinaryAdd($num1,$num2) { $carry = $num1 & $num2; do { $carry = $carry « 1; $num1 = $num1 ^ $num2; $sum = $num1 ^ $carry; $num2 = $carry; $carry = $num1 & $num2; } while($carry != 0); return $sum; } function BinarySubtract($num1,$num2) { Compute Two’s Compliment $num2 = ~ $num2; $num2 = BinaryAdd($num2,1); $diff = BinaryAdd($num1,$num2); return $diff; } Class to wrap the functions class BinaryMath { var $x; var $y; var $result; function BinaryMath ($x,$y) { $this→x=$x; $this→y=$y; } function call($method,$params,&$result) { $this→result=$method($this→x,$this→y); return true; } } overload (”BinaryMath”); Use the functions via the class API $binaryMath=new BinaryMath (15,5);

$binaryMath→BinaryAdd(); echo ( “Sum is: “.$binaryMath→result.”<br />” );

$binaryMath→BinarySubtract(); echo ( “Difference is: “.$binaryMath→result.”<br />” ); ?> </code>

Wrap Up

Although it’s worth noting that word “experimental” in the PHP manual, the overload extension can be used to extend our applications at minimal coding effort.

With Zend 2 it seems that overloading functionality will be build directly into the core of PHP, as Zeev Suraski suggests in The Object-Oriented Evolution of PHP;

PHP 4.0 was the first version to introduce this sort of integration, but the new implementation is much quicker, more complete, more reliable and far easier to maintain and extend. These elements mean that PHP 5.0 will play very nicely in your existing Java or .NET based setup. You’ll be able to use your existing components inside PHP transparently, as if they were regular PHP objects. Unlike PHP 4.0, which had a special implementation for such overloaded objects, PHP 5.0 uses the same interface for all objects, including native PHP objects. This feature ensures that PHP objects and overloaded objects behave in exactly the same way.”

Given that overloading is being backed in Zend 2, it’s likely to become a very powerful and widely used PHP coding technique.


develop/extending_php_classes_with_overload.txt · Last modified: 2005/10/15 21:47