PHP Overloading

Overloading in PHP describes the way in which properties and methods of an object can be dynamically created or accessed without having to define them first. Traditionally, the word overloading in programming is used to describe accessing object methods with the same name but with different parameters. It is not possible to do this in PHP as it will complain about methods having the same name, so the term describes calling a method or accessing a property that hasn't previously been set or is out of scope. In object orientated terms this means that the method or property is private.

Object Property Overloading

Property overloading allows you to access the property of an object through a method without having to write them first. It can also be used to access any properties that are inaccessible. There are two basic property overloading methods available, these are __set() and __get(), both contain a double underscore (_) in their name.

The __set() method is called whenever an undefined or inaccessible property of an object is set and must have the following footprint.

void __set ( string $name , mixed $value )

The __get() method is called whenever an undefined or inaccessible property of an object is read and must have the following footprint.

mixed __get ( string $name )

The following simple class defines both these functions in their simplest form along with a couple of properties. Every time either the __set() or __get() methods are used they will print out some information.

class OverloadingTest
{
    private $privateProperty;
 
    public $publicProperty;
    
    public function __set($name, $value)
    {
        echo '__set() ' . $name . ' ' . $value. "\n";
        $this->$name = $value;
    }
    
    public function __get($name)
    {
        echo '__get() ' . $name . "\n";
        return $this->$name;
    }
}

To test these functions I wrote some simple code.

$object = new OverloadingTest();
$object->privateProperty = 'this is a private value';
echo $object->privateProperty . "\n";
 
$object->publicProperty = 'this is a publicvalue';
echo $object->publicProperty . "\n";
 
$object->nonExistingProperty = 'another value';
echo $object->nonExistingProperty . "\n";
 
var_dump($object);

This code creates the following output.

__set() privateProperty this is a private value
__get() privateProperty
this is a private value
this is a publicvalue
__set() nonExistingProperty another value
another value
object(OverloadingTest)#1 (3) {
  ["privateProperty:private"]=>
  string(23) "this is a private value"
  ["publicProperty"]=>
  string(21) "this is a publicvalue"
  ["nonExistingProperty"]=>
  string(13) "another value"
}

One very important thing to note is that when a property is created using __set(), the __get() is not used to retrieve the value of the property. For this reason you might see some classes created like this, where a private property is created as an array and the __get() and __set() methods access this property.

class OverloadingTest2
{
    private $privateProperty = array();
    
    public function __set($name, $value)
    {
        echo '__set() ' . $name . ' ' . $value. "\n";
        $this->privateProperty[$name] = $value;
    }
    
    public function __get($name)
    {
        echo '__get() ' . $name . "\n";
        if (!isset($this->privateProperty[$name])) {
            return null;
        }
        return $this->privateProperty[$name];
    }
}

This forces the __get() method to be used when reading the property of the parameter, rather than it being ignored as in the previous example. The outside code looks very similar to the previous code, but the class that controls the data stores it in a different way.

$object = new OverloadingTest2();
$object->property = 'this is a value';
echo $object->property . "\n";
 
var_dump($object);

This produces the following output.

__set() property this is a private value
__get() property
this is a private value
object(OverloadingTest)#1 (1) {
  ["property:private"]=>
  array(1) {
    ["property"]=>
    string(15) "this is a value"
  }
}

If you are used to object orientation you might have seen something called chaining, especially where set type methods are used. This doesn't work using the __set() method as any return values are ignored and the $value parameter sent to the method is returned instead. The following method would return the value of the $value parameter an NOT the current object.

public function __set($name, $value)
{
    echo '__set() ' . $name . ' ' . $value . "\n";
    $this->privateProperty[$name] = $value;
    return $this;
}

Two other property overloading methods exist, these are called __isset() and __unset(), which have been available since PHP version 5.1.0. The __isset() method is called when isset() or empty() is called on innacessible properties and must have the following footprint.

bool __isset ( string $name )

The __unset() method is called when the unset() function is called on inaccessible properties and has the following footprint.

void __unset ( string $name )

With this information in hand we can modify our class to include these functions.

class OverloadingTest3
{
    private $privateProperty = array();
    
    public function __set($name, $value)
    {
        echo '__set() ' . $name . ' ' . $value. "\n";
        $this->privateProperty[$name] = $value;
        return $this;
    }
    
    public function __get($name)
    {
        echo '__get() ' . $name . "\n";
        if (!isset($this->privateProperty[$name])) {
            return null;
        }
        return $this->privateProperty[$name];
    }
    
    public function __isset($name) {
        echo '__isset() ' . $name . "\n";
        return isset($this->privateProperty[$name]);
    }
 
    public function __unset($name) {
        echo '__unset() ' . $name . "\n";
        unset($this->privateProperty[$name]);
    }
}

We can test these new functions like this.

$object = new OverloadingTest3();
$object->property = 'this is a value';
echo $object->property . "\n";
 
var_dump(isset($object->property));
unset($object->property);
var_dump(isset($object->property));
 
var_dump($object);

This produces the following output.

__set() property this is a value
__get() property
this is a value
__isset() property
bool(true)
__unset() property
__isset() property
bool(false)
object(OverloadingTest3)#1 (1) {
  ["privateProperty:private"]=>
  array(0) {
  }
}

Object Method Overloading

Method overloading works in much the same way as property overloading, but is obviously used on non-existent or inaccessible methods. By adding a method called __call() to your class you can intercept any calls to inaccessible methods. The __call() method must have the following footprint.

mixed __call ( string $name , array $arguments )

The $name parameter will contain the name of the function called and is case sensitive, the $arguments parameter will contain an array of any arguments sent to the method call. The following is a very simple example of this method in action.

class OverloadMethodTest
{
    public function __call($name, $arguments)
    {
        echo '__call ' . $name . "\n";
        print_r($arguments);
    }
}
 
$obj = new OverloadMethodTest();
$obj->runSomething('parameter1', 2);

This will produce the following output.

__call runSomething
Array
(
    [0] => parameter1
    [1] => 2
)

Out of interest I wondered what would happen if I added a private method called runSomething() to the class and tried to run the same code.

class OverloadMethodTest
{
    public function __call($name, $arguments)
    {
        echo '__call ' . $name . "\n";
        print_r($arguments);
    }
 
    private function runSomething()
    {
        echo 'runSomething() is a private method';
    }
}
 
$obj = new OverloadMethodTest();
$obj->runSomething('parameter1', 2);

This produced exactly the same output as before, which means that the __call() method intercepts any calls to existing private methods rather than produce a fatal error about accessing private methods.

As of PHP 5.3.0 there is also a method called __callStatic() that can be used to provide the same sort of functionality to static method calls that __call() provides. The __callStatic() method also has the same footprint as the __call() method and is called in the same way.

mixed __callStatic ( string $name , array $arguments )

Here is a simple class that uses __callStatic().

class OverloadMethodTest
{
    public function __call($name, $arguments)
    {
        echo '__call ' . $name . "\n";
        print_r($arguments);
    }
    
    public static function __callStatic($name, $arguments)
    {
        echo '__callStatic ' . $name . "\n";
        print_r($arguments);
    }
}
 
$obj = new OverloadMethodTest();
$obj->runSomething('parameter1', 2);
OverloadMethodTest::runSomething('in static context');

This produces the following output.

__call runSomething
Array
(
    [0] => parameter1
    [1] => 2
)
__callStatic runSomething
Array
(
    [0] => in static context
)

There is one thing you can't call a method that would go through the __call() method from within the __call() method. In other words, recursive calls to __call() don't work. You can try running it yourself to see what happens using this code.

class OverloadMethodTest2
{
    public function __call($name, $arguments)
    {
        echo '__call ' . $name . "\n";
        $this->wibble($name);
        print_r($arguments);
    }
}
 
$obj = new OverloadMethodTest2();
$obj->runSomething('parameter1', 2);

You will simply receive a null response from your server when running this code.

It is possible to call inaccessible methods from within the same object, you just need to do this from a different method. The following code shows an example of this that works.

class OverloadMethodTest3
{
    public function __call($name, $arguments)
    {
        echo '__call ' . $name . "\n";
        print_r($arguments);
    }
    
    public function test()
    {
        $this->runSomething('parameter1', 2);
    }
}
 
$obj = new OverloadMethodTest3();
$obj->test();

This produces the following output.

__call runSomething
Array
(
    [0] => parameter1
    [1] => 2
)

This makes sense as the code runs the test() method, which in turn runs the runSomething() method.

Of course it can be a little dangerous to simply allow your classes to process any method name that is run against them. For this reason it is best to employ a switch statement inside the __call() method in order to stop any methods being run that you haven't allowed for. The following example shows this in action.

class OverloadMethodTest4
{
    public function __call($name, $arguments)
    {
        switch ($name) {
            case 'function1':
                    // do something with $arguments
                break;
        }
    }
}

If you want to know more about overloading then you can take a look at the overloading manual page on the PHP website.

Comments

Thanks for this, it really helped me learn about overloading in php, I had no prior overloading experience and I though it was well explained.. "bookmarked"
Permalink
Really this articale is so much helpfull for me to understanding the Overloading the properties in php...Can you explain me about the stdClass() in php... Thanking you much... Regards
Permalink

Glad you liked it. The stdClass(), since you asked, is a generic empty class that can be used anywhere in PHP. It doesn't have any methods or properties of its own and is intended to be used as a container.

It can be used right out of the box like this:

$object = new stdClass();
$object->property = 'wibble';

Or is can also created when you cast anything as an object, like this:

$array = array('property' => 'wibble');
$object = (object)$array;
echo $object->property;

Actually, it does have one property called "scalar", which is created when a string is cast as an object, like this:

$object = (object)'wibble';
echo $object->scalar; // prints 'wibble'
Name
Philip Norton
Permalink

Good Article. Excellent

Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
11 + 4 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.