Text/Compiled by Zhu Xianzhong
1. Introduction
Fortunately, object overloading technology was introduced in PHP 5.0. This article will explore the possibility of overloading the methods __call(), __set() and __get(). After a brief introduction to overloading theory, we will go straight to the topic through two examples: the first example is to implement a persistent storage class; the second example is to find a way to implement dynamic getter/setter.
2. What is object overloading?
When talking about object overloading in PHP, we have to distinguish two types:
·Method overloading
·Attribute overloading
In the case of method overloading, we have to define a magic method __call(), which will implement a A generic call to an undefined method in the corresponding class. This general method is called only when you want to access an undefined method in the class. Without method overloading, the following example will cause PHP to display a fatal error message: Call to undefined method ThisWillFail::bar() in/some/directory/example.php on line 9 and abort execution of the program:
< ?php
class ThisWillFail {
public function foo() {
return "Hello World!";
}
}
$class = new ThisWillFail;
$class->bar();
?>
With the help of method overloading, the code can catch this call and handle it gracefully.
Property overloading is similar to method overloading. In this case, the class redirects (also called proxying) read/write operations to properties of the class that are not explicitly defined in the class. The specialized methods here are __set() and __get(). Depending on the error reporting level, the PHP translator will usually either issue a notification when accessing an undefined property, or defer and potentially define the variable. If you use attribute overloading, the translator can call __set() when setting an undefined attribute, and call __get() when accessing an undefined attribute value.
To sum up, the use of overloading technology can greatly shorten the software development time when using dynamic languages such as PHP.
At this point, the theory is introduced, and the specific coding is analyzed below.
3. Examples of persistent storage classes
The following code implements the persistent storage class mentioned above with less than 50 lines of PHP code by using attribute overloading technology. The term persistent means that the class can describe an element from a data structure and remain synchronized with the underlying storage system. In coding terms, external code can use classes to select a row from a database table. In this way, when the program is running, you can directly access the attributes of the class to manipulate the elements in the row (read/fetch). At the end of the script, PHP will be responsible for sending the updated row data back to the database.
Carefully studying the following code will help you understand what attribute overloading is.
<?php
//Load PEAR's <a href=" http://pear.php.net/package/DB/ "> DB package</a>
require_once "DB.php";
class Persistable {
private $data = array();
private $table = "users";
public function __construct($user) {
$this->dbh = DB::Connect("mysql://user:password@localhost/database");
$query = "SELECT id, name, email, country FROM " .
$this->table . " WHERE name = ?";
$this->data = $this->dbh->getRow($query, array($user),
DB_FETCHMODE_ASSOC);
}
public function __get($member) {
if (isset($this->data[$member])) {
return $this->data[$member];
}
}
public function __set($member, $value) {
//The ID of the dataset is read-only if ($member == "id") {
return;
}
if (isset($this->data[$member])) {
$this->data[$member] = $value;
}
}
public function __destruct() {
$query = "UPDATE " . $this->table . " SET name = ?,
email = ?, country = ? WHERE id = ?";
$this->dbh->query($query, $this->name, $this->email,
$this->country, $this->id);
}
}
$class = new Persistable("Martin Jansen");
$class->name = "John Doe";
$class->country = "United States";
$class->email = " [email protected] ";
?>
The first problem you may encounter is __construct(), the new constructor method introduced in PHP 5. In the days of PHP 4, constructors always matched their class names. This is no longer the case in PHP 5. You don't need to know much about the constructor method, except that calling it creates an instance of a class; and notice that a parameter is used here - a database is executed based on this parameter. This constructor assigns the query results to the class attribute $data.
Next, the program defines two special methods __get() and __set(). You should already be familiar with them: __get() is used to read undefined attribute values, and __set() is used to modify undefined attribute values.
This means that whenever an undefined property is read/written from a persistent storage class, these specialized methods are responsible for managing the information in the property array variable $data, rather than directly changing the properties of the class (remember: The variable $data contains a row from the database!).
The last method in a class is the opposite of __construct() - the destructor __destruct(). PHP calls the destructor during the "script shutdown phase", which is typically near the end of a PHP script's execution. The destructor writes the information from the $data attribute back to the database. This is exactly what the previous term synchronization means.
You may have noticed that the code here uses PEAR's database abstraction layer package. In fact, it doesn't matter. Communicating with the database through other methods can also illustrate the theme of this article.
If you look carefully, you will find that the description of this persistent storage class is relatively simple. The example only involves a database table, and does not consider more complex data models, such as the use of LEFT JOIN and other complex database operation techniques. However, you don't have to be bound by this, and with the help of property overloading, you can use your own ideal database model. With just a little bit of code, you can take advantage of complex database features in this persistent storage class.
There is also a small problem - no error handling mechanism is introduced when the query fails in the destructor. It is the nature of destructors that makes it impossible to display an appropriate error message in this case, because building the HTML markup often ends before PHP calls the destructor.
To solve this problem, you can rename __destruct() to something like saveData() and execute this method manually somewhere in the calling script. This doesn't change the concept of persistent storage for classes; it's just a few more lines of code. Alternatively, you can use the error_log() function in the destructor to log the error message to a system-wide error log file.
This is how property overloading works. Next we discuss method overloading.
4. Examples of method overloading
1. Dynamic Getter/Setter methods
The following code implements the "dynamic" getter/setter methods to control the class with the help of method overloading. Let's analyze it based on the source code:
<?php
class DynamicGetterSetter {
private $name = "Martin Jansen";
private $starbucksdrink = "Caramel Cappuccino Swirl";
function __call($method, $arguments) {
$prefix = strtolower(substr($method, 0, 3));
$property = strtolower(substr($method, 3));
if (empty($prefix) || empty($property)) {
return;
}
if ($prefix == "get" && isset($this->$property)) {
return $this->$property;
}
if ($prefix == "set") {
$this->$property = $arguments[0];
}
}
}
$class = new DynamicGetterSetter;
echo "Name: " . $class->getName() . "n";
echo "Favourite Starbucks flavor: " . $class->getStarbucksDrink() . "nn";
$class->setName("John Doe");
$class->setStarbucksDrink("Classic Coffee");
echo "Name: " . $class->getName() . "n";
echo "Favourite Starbucks flavor: " . $class->getStarbucksDrink() . "nn";
?>
Obviously, the two attributes $name and $starbucksdrink here are private, which means that these attributes cannot be accessed from outside the class. In object-oriented programming, it is very common to implement public getter/setter methods to access or modify the values of non-public properties. Implementing these is tedious and time and effort consuming.
This problem can be easily solved with the help of method overloading. Instead of implementing getter/setter methods for each property, the above only implements a general __call() method. This means that when an undefined getter/setter method such as setName() or getStarbucksdrink() is called, PHP will not generate a fatal error and abort, but instead execute (or delegate to) the magic __call() method.
These are some brief introductions. Let’s do an in-depth analysis of __call().
2. Detailed analysis of the __call() method.
The first parameter of __call() is the original and undetermined method (such as setName). The second parameter is a one-dimensional array with a numerical index, which contains all the original methods. parameter. Calling an undefined method with two parameters ("Martin" and 42) will produce the following array:
$class->thisMethodDoesNotExist("Martin", 42);
/Guide to the second parameter of __call()
Array
(
[0] =>Martin
[1] => 42
)
Inside the method __call(), if the original method starts with get or set, some calculation must be performed to determine whether the code calls a getter/setter method. Moreover, this method will further analyze another component of the method name (except for the first three characters), because the latter part of the string represents the name of the attribute referenced by the getter/setter.
If the method name indicates a getter/setter, then the method either returns the corresponding property value or sets the value of the first parameter of the original method. If not, it does nothing and continues executing the program as if nothing happened.
3. To achieve the goal
In essence, corresponding to any attribute, there is a method that allows the code to dynamically call any getter/setter method. This algorithm exists. This is convenient when developing a program prototype in the short term: instead of spending a lot of time implementing getters/setters, the developer can focus on modeling the API and ensuring that the application is fundamentally correct. Incorporating the __call() method into an abstract class may even enable you to reuse code in future PHP project development!
4. In addition to shortcomings,
there are advantages and disadvantages. There are several drawbacks to the above approach: Larger projects may use tools like phpDocumentor to track the API structure. With the dynamic method introduced above, of course all getter/setter methods will not appear in the automatically generated document, which requires no further explanation.
Another drawback is that code outside the class can access every private property within the class. When using real getter/setter methods, it is possible to distinguish between private properties that can be accessed by external code and "real" private properties that are not visible outside the class - because we have method overloading, and we have virtual getters and setters Methods can be used.
5. Conclusion
This article carefully analyzes the two situations of object overloading in PHP 5.0 through two examples. I very much hope that the method in this article can help you improve the efficiency of PHP programming! At the same time, you should also clearly see the shortcomings of this method.