- Introduction
- Overloading Capabilities
- Iterators
- Design Patterns
- Reflection
- Summary
4.2 Overloading Capabilities
In PHP 5, extensions written in C can overload almost every aspect of the object syntax. It also allows PHP code to overload a limited subset that is most often needed. This section covers the overloading abilities that you can control from your PHP code.
4.2.1 Property and Method Overloading
PHP allows overloading of property access and method calls by implementing special proxy methods that are invoked if the relevant property or method doesn't exist. This gives you a lot of flexibility in intercepting these actions and defining your own functionality.
You may implement the following method prototypes:
function __get($property) function __set($property, $value) function __call($method, $args)
__get is passed the property's name, and you should return a value.
__set is passed the property's name and its new value.
__call is passed the method's name and a numerically indexed array of the passed arguments starting from 0 for the first argument.
The following example shows how to use the __set and __get functions ( array_key_exists() is covered later in this book; it checks whether a key exists in the specified array):
class StrictCoordinateClass { private $arr = array('x' => NULL, 'y' => NULL); function __get($property) { if (array_key_exists($property, $this->arr)) { return $this->arr[$property]; } else { print "Error: Can't read a property other than x & y\n"; } } function __set($property, $value) { if (array_key_exists($property, $this->arr)) { $this->arr[$property] = $value; } else { print "Error: Can't write a property other than x & y\n"; } } } $obj = new StrictCoordinateClass(); $obj->x = 1; print $obj->x; print "\n"; $obj->n = 2; print $obj->n;
The output is
1 Error: Can't write a property other than x & y Error: Can't read a property other than x & y
As x exists in the object's array, the setter and getter method handlers agrees to read/write the values. However, when accessing the property n , both for reading and writing, array_key_exists() returns false and, therefore, the error messages are reached.
__call() can be used for a variety of purposes. The following example shows how to create a delegation model, in which an instance of the class HelloWorldDelegator delegates all method calls to an instance of the HelloWorld class:
class HelloWorld { function display($count) { for ($i = 0; $i < $count; $i++) { print "Hello, World\n"; } return $count; } } class HelloWorldDelegator { function __construct() { $this->obj = new HelloWorld(); } function __call($method, $args) { return call_user_func_array(array($this->obj , $method), $args); } private $obj; } $obj = new HelloWorldDelegator(); print $obj->display(3);
This script's output is
Hello, World Hello, World Hello, World 3
The call_user_func_array() function allows __call() to relay the function call with its arguments to HelloWorld::display() which prints out "Hello, World\n" three times. It then returns $count (in this case, 3 ) which is then printed out. Not only can you relay the method call to a different object (or handle it in whatever way you want), but you can also return a value from __call() , just like a regular method.
4.2.2 Overloading the Array Access Syntax
It is common to have key/value mappings or, in other words, lookup dictionaries in your application framework. For this purpose, PHP supports associative arrays that map either integer or string values to any other PHP value. This feature was covered in Chapter 2, "PHP 5 Basic Language," and in case you forgot about it, here's an example that looks up the user John's social-security number using an associative array which holds this information:
print "John's ID number is " . $userMap["John"];
Associative arrays are extremely convenient when you have all the information at hand. But consider a government office that has millions of people in its database; it just wouldn't make sense to load the entire database into the $userMap associative array just to look up one user. A possible alternative is to write a method that will look up the user's id number via a database call. The previous code would look something like the following:
print "John's ID number is " . $db->FindIDNumber("John");
This example would work well, but many developers prefer the associative array syntax to access key/value-like dictionaries. For this purpose, PHP 5 enables you to overload an object so that it can behave like an array. Basically, it would enable you to use the array syntax, but behind the scenes, a method written by you would be called, which would execute the relevant database call, returning the wanted value.
It is really a matter of personal preference as to what method to use. Sometimes, it is nicer to use this overloading ability than the verbosity of calling a method, and it's up to you to decide which method suits you best.
To allow your class to overload the array syntax, it needs to implement the ArrayAccess interface (see Figure 4.1).
Figure 4.1 ArrayAccess interface.
The following example shows how to use it. It is incomplete because the database methods themselves aren't implemented:
class UserToSocialSecurity implements ArrayAccess { private $db; // An object which includes database access methods function offsetExists($name) { return $this->db->userExists($name); } function offsetGet($name) { return $this->db->getUserId($name); } function offsetSet($name, $id) { $this->db->setUserId($name, $id); } function offsetUnset($name) { $this->db->removeUser($name); } } $userMap = new UserToSocialSecurity(); print "John's ID number is " . $userMap["John"];
You can see that the object $userMap is used just like an array, but behind the scenes, when the $userMap["John"] lookup is performed, the offsetGet() method is invoked, which in turn calls the database getUserId() method.