0
Php

Php Dynamic Getter & Setter by Overloading

In Php there are many useful magic methods and two of those are __call() and __callStatic() methods. These magic methods are similar to __get() and __set() but only difference is that these are for class methods instead of class properties (variables). If we call an undefined method or a method that is out of scope, the __call() method will be invoked automatically, if it is defined in the class. The prototype of the __call() magic method is

function __call($methodName, $params)

When this function is invoked (for an inaccessible method), it’ll contain the calling method name and method’s arguments in $methodName and $params respectively. Which means, If we call a method, for example, $obj->nonExistent('anArgument') and if this method is not defined in our class, then the __call() method will be invoked and in the method, we can get the name of the calling method, in this case nonExistent in the first argument and it’s parameters in the second argument as an array (if provided). for example, if we have following class

class MyClass{
    public function doSomething()
    {
        echo "Method 'doSomething' has been called!";
    }

    public function __call($methodName, $params = null)
    {
        echo "Method '" . $methodName ."' has been called with " . count($params) . " parameter(s)!";
    }
}

Now, if we do something like this

$obj = new MyClass;
$obj->nonExistent('anArgument');

The result will be Method nonExistent has been called with 1 parameter(s)!, here $params[0] will contain anArgument, so, if we call it with two parameters then $params array will contain two items in it. well, it’s very simple.

Now, we can use it as a dynamic setter and getter instead of __set() and __get() and we can use only this function for as many properties as we want. Let’s see an example

class MyClass{
    protected $settings = array();
    public function __call($methodName, $params = null)
    {
        if($methodName == 'set' && count($params) == 2)
        {
            $key = $params[0];
            $value = $params[1];
            $this->settings[$key] = $value;
        }
        elseif($methodName == 'get' && count($params) == 1)
        {
            $key = $params[0];
            if(array_key_exists($key, $this->settings)) return $this->settings[$key];
        }
        else
        {
            exit('Opps! The method is not defined!');
        }
    }
}

This method is very useful to use it as a dynamic getter and setter for any number of classes and we need to write only one __call() function for all classes. Lets write a simple example first.

$obj = new MyClass;
$obj->set('name', 'Sheikh Heera');
echo $obj->get('name'); // Sheikh Heera

Well, this is a very simple example and we should handle errors in a better way. Anyways, We can improve this and after some changes we can use it for all of our classes like

$obj = new MyClass;
$obj->setName('Sheikh Heera');
echo $obj->getName();

So, lets improve it a bit by changing the code of MyClass

abstract class MyClass{
    
    public function __call($methodName, $params = null)
    {
        $methodPrefix = substr($methodName, 0, 3);
        $key = strtolower(substr($method, 3));
        if($methodPrefix == 'set' && count($params) == 1)
        {
            $value = $params[0];
            $this->settings[$key] = $value;
        }
        elseif($methodPrefix == 'get')
        {
            if(array_key_exists($key, $this->settings)) return $this->settings[$key];
        }
        else
        {
            exit('Opps! The method is not defined!');
        }
    }
}

Now, we can create sub-classes and use them like

// Class Test1
class Test extends MyClass{
    protected $settings = array();
}
// Class Test1
class Test2 extends MyClass{
    protected $settings = array();
}
// Use Test1
$obj1 = new Test1;
$obj1->setName('Sheikh Heera');
$obj1->setage(30);
echo $obj1->getName(); // Sheikh Heera
echo $obj1->getAge(); // 30
  
// Use Test2
$obj2 = new Test2;
$obj2->setName('John Doe');
echo $obj2->getName(); // John Doe

This is really very helpful. By writing a single abstract class (it’s not required to make it abstract) we can use it for as many classes as we want and this way we can save our time.

In Php, this is known as Overloading and it is very useful to dynamically “create” properties and methods for a class and there are different methods available for property overloading, such as, __get() and __set() (I’ve mentioned earlier), which are invoked when interacting with properties that have not been declared or are not visible in the current scope and for method overloading the __call() magic method is invoked every time when someone calls a non-existent class method but only for non-static method calls. For static method overloading, Php invokes __callStatic() magic method instead of __call() as from PHP 5.3.0 but there is no difference, we can use __callStatic() the same way we use __call(). Well, it’s better to see an example of static method call, here it is

class MyClass{
    public static function doSomething()
    {
        // ...
    }

    public static function __callStatic($methodName, $arguments)
    {
        // This will be invoked for a non existent static method call
    }
}

In this case, our doSomething() method is a static method and we can call it like

MyClass::doSomething() // static method call
[/code]
If we call something like this
MyClass::doSomethingElse('foo') // non existent static method call

Then, our __callStatic() magic method will be invoked and $methodName will contain doSomethingElse and $arguments will contain the parameters used in the method, as an array like this Array ( [0] => foo ), where foo will be the first (zero index) item in the array.

Remember that, property overloading only works in object context. Magic methods like __set() and __get() will not be triggered in static context. Therefore these methods should not be declared static. As of PHP 5.3.0, a warning is issued if one of the magic overloading methods is declared as static and all overloading methods must be defined as public.

One of the most interesting and most practical use of method overloading using __callStatic() magic method is that, you can use a fake class to call methods of another class and this is a practical Php design pattern and it's known as Facade. In Facade pattern, a Facade is a class that provides a unified interface to a subsystem, which is essentially an API – a nice and client-facing interface, which hides the concrete classes from the client. This pattern is usually implemented to reduce coupling between subsystems and towards vendor code; libraries often provide a Facade to hide their internals and being able to change them in subsequent releases. In Laravel 4 php framework this design pattern has been used and that's why we can use something like

$value = Cache::get('key');

It seems that, class Cache has a static method get() but it's not true, behind the scene, there is a __callStatic() method invoked when we call Cache::get('key') and from that magic method the original class become instantiated and the method get called, for example, something like this happens when someone calls Cache::get('key'), because the method get doesn't exist in the Cache class, so __callStatic() invoked for non-existent static method call (pseudo example)

class Cache extends Facade {
    
    protected static function getClassName() { return 'cache'; }
    
    public static function __callStatic($method, $args)
    {
        $class = static::getClassName();
        $instanse = new $class;
        return call_user_func_array(array($instance, $method), $args);
    }

}

Well, this is just a pseudo example of Facade, but originally something more happens behind the scene. This article is not about design patterns instead, it's about method overloading using __call() and __callStatic() magic methods. So, not going further but you can read more about this here, that's it.

Latest Blog

0
Php

PHP – 8.0 Match Expression

In PHP 8.0 there is a new feature or I should say a new language construct (keyword) going to be introduced,  which has been implemented depending […]

0
Php

New Union Type in PHP – 8

A new RFC has been finished voting and accepted for PHP – 8. That is “Union Types”, which is an extension of PHP’s type system. […]