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 thisMyClass::doSomethingElse('foo') // non existent static method callThen, our
__callStatic()
magic method will be invoked and$methodName
will containdoSomethingElse
and$arguments
will contain the parameters used in the method, as an array like thisArray ( [0] => foo )
, wherefoo
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 ofPHP 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 practicalPhp
design pattern and it's known asFacade
. InFacade
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. InLaravel 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 methodget()
but it's not true, behind the scene, there is a__callStatic()
method invoked when we callCache::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 callsCache::get('key')
, because the methodget
doesn't exist in theCache
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.