In the last section of this chapter, Zeev discusses the object model brought by the Zend engine, and specifically mentions how it differs from the models in the previous versions of PHP.
When we developed PHP3 in the summer of 1997, we had no plans to make PHP object-oriented. There was no idea about classes and objects at that time. PHP3 is a purely process-oriented language. However, in the evening of 1997.8.27, support for classes was added to the PHP3 alpha version. Adding a new feature to PHP only required very little discussion at that time, because there were too few people exploring PHP at that time. So from August 1997, PHP took the first step towards an object-oriented programming language.
Indeed, this is just the first step. Because there are very few related ideas in this design, the support for objects is not strong enough. Using objects in this version is just a cool way to access arrays. Instead of using $foo["bar"], you can use the more beautiful $foo->bar. The main advantage of the object-oriented method is to store functions through member functions or methods. A typical code block is shown in Example 6.18. But it is not much different from the ones in Example 6.19.
Listing 6.18 PHP 3 object-oriented programming object-oriented programming in PHP3
<?php
class Example
{
var $value = "some value";
function PrintValue()
{
print $this->value;
}
}
$obj = new Example();
$obj->PrintValue();
?> Listing 6.19 PHP 3 structural programming PHP3 Structured programming in PHP3
<?php
function PrintValue($arr)
{
print $arr["value"];
}
function CreateExample()
{
$arr["value"] = "some value";
$arr["PrintValue"] = "PrintValue";
return $arr;
}
$arr = CreateExample();
//Use PHP's indirect reference
$arr["PrintValue"]($arr);
?> We write two lines of code in the class above, or pass the array to the function displayably. But considering that the two options in PHP3 are not different, we can still access the array only by treating the object model as a "grammatical embellishment".
People who want to use PHP for object-oriented development, especially those who want to use design patterns, soon found that they were hitting a wall. Fortunately, at that time (the PHP3 era), not many people wanted to use PHP for object-oriented development.
PHP4 changed this situation. The new version brought the concept of reference, which allows different identifiers of PHP to point to the same address in memory. This means that you can use two or more names to name the same variable, like in Example 6.20.
Listing 6.20 PHP 4 references References in PHP4
<?php
$a = 5;
//$b points to the same place in memory as $a $b and $a point to the same address in memory
$b = &$a;
//we're changing $b, since $a is pointing to change $b, the address pointed to change
//the same place - it changes too $a also changes
$b = 7;
//prints 7 output 7
print $a;
?> Since building a network of objects that point to each other is the basis of all object-oriented design patterns, this improvement is of great significance. When references allow more powerful object-oriented applications, PHP treats objects and other types of data the same way brings great pain to developers. As any PHP4 programmer will tell you, the application will suffer from WTMA (Way Too Many Ampersands) syndrome. If you want to build a practical application, you will feel extremely painful, look at Example 6.21 and you will understand.
Listing 6.21 Problems with objects in PHP 4 Problems with objects in PHP4
1 class MyFoo {
2 function MyFoo()
3 {
4 $this->me = &$this;
5 $this->value = 5;
6 }
7
8 function setValue($val)
9 {
10 $this->value = $val;
11 }
12
13 function getValue()
14 {
15 return $this->value;
16 }
17
18 function getValueFromMe()
19 {
20 return $this->me->value;
21 }
22 }
23
24 function CreateObject($class_type)
25 {
26 switch ($class_type) {
27 case "foo":
28 $obj = new MyFoo();
29 break;
30 case "bar":
31 $obj = new MyBar();
32 break;
33 }
34 return $obj;
35 }
36
37 $global_obj = CreateObject ("foo");
38 $global_obj->setValue(7);
39
40 print "Value is " . $global_obj->getValue() . "n";
41 print "Value is " . $global_obj->getValueFromMe() . "n"; Let's discuss it step by step. First, there is a MyFoo class. In the constructor, we give $this->me a reference and set it
We have three other member functions: one sets the value of this->value; one returns the value of this->value; the other returns the value of this->value->me. But isn't $this the same thing? Are the values returned by MyFoo::getValue() and MyFoo::getValueFromMe() not the same?
First, we call CreateObject("foo"), which returns an object of type MyFoo. Then we call MyFoo::setValue(7). Finally, we call MyFoo::getValue() and MyFoo::getValueFromMe(), hoping to get the return value 7.
Of course, if we get 7 in any case, the above example will not be the most meaningless example in this book. So I believe you have guessed that we can't get two such results.
But what are the results we will get, and more importantly, why?
The results we will get are 7 and 5 respectively. As for why — there are three good reasons.
First, look at the constructor. When inside the constructor, we establish references between this and this->me. In other words, this and this->me are the same thing. But we are inside the constructor. When the constructor ends, PHP needs to re-establish the object (the result of new MyFoo, line 28) to assign it to $obj. Because the object is not treated with special treatment, just like any other data type, assigning X to Y means Y to Y is a copy of X. That is, obj will be a copy of new MyFoo, and new MyFoo is an object that exists in the constructor. How about Obj->me? Because it is a reference, it still points to the original object—this. Voila-obj and obj->me are no longer the same thing—change one of the other.
The above is the first reason. There are other reasons similar to the first one. Miraculously we intend to overcome the problem of instantiating objects (line 28). Once we assign the value returned by CreateObject to global_object, we still have to run into the same problem—global_object will become a copy of the return value, and again, global_object and global_object->me will not be the same anymore. This is the second reason.
However, in fact we can't go that far - once CreateObject returns $obj, we will break the reference (line 34). This is the third reason.
So, how do we correct these? There are two options. One is to add the & symbols everywhere, like in Example 6.22 (lines 24, 28, 31, 37). Two. If you are lucky enough to use PHP5, you can forget all of the above, and PHP5 will automatically consider these for you. If you want to know how PHP5 considers these issues, continue reading.
Listing 6.22 WTMA syndrome in PHP 4 WTMA syndrome in PHP4 1 class MyFoo {
2 function MyFoo()
3 {
4 $this->me = &$this;
5 $this->value = 2;
6 }
7
8 function setValue($val)
9 {
10 $this->value = $val;
11 }
12
13 function getValue()
14 {
15 return $this->value;
16 }
17
18 function getValueFromMe()
19 {
20 return $this->me->value;
21 }
22 };
23
24 function &CreateObject($class_type)
25 {
26 switch ($class_type) {
27 case "foo":
28 $obj =& new MyFoo();
29 break;
30 case "bar":
31 $obj =& new MyBar();
32 break;
33 }
34 return $obj;
35 }
36
37 $global_obj =& CreateObject ("foo");
38 $global_obj->setValue(7);
39
40 print "Value is " . $global_obj->getValue() . "n";
41 print "Value is " . $global_obj->getValueFromMe() . "n"; PHP5 is the first PHP version to regard an object as a different PHP version from other types of data. From the user's perspective, this proves that it is very clear - in PHP5, objects are always passed through references, while other types of data (such as integer, string, array) are passed through values. Most significantly, there is no need to use the & symbol to represent the object passing through references.
Object-oriented programming widely utilizes the complex relationship between the object network and objects, all of which require references. In previous versions of PHP, references need to be indicated displayably. Therefore, references are now used to move objects by default, and objects are copied only when copying is explicitly required, which is better than before.
How is it implemented?
Before PHP5, all values existed in a special structure called zval(Zend Value). These values can be stored in simple values, such as numbers and strings, or complex values such as arrays and objects. When values are passed to or returned from functions, these values are copied to create a structure with the same content at another address in memory.
In PHP5, the value remains in the zval structure, except for the object. Objects exist in a structure called the Object Store, and each object has a different ID. In Zval, the object itself does not store the object itself, but the object pointer. When copying a zval structure that holds the object, for example, we pass an object as a parameter to a function, we no longer copy any data. We simply keep the same object pointer and inform the Object Store that is pointed to by another zval. Because the object itself is located in the Object Store, any changes we make to it will affect all zval structures that hold the object pointer. This additional indirect effect makes PHP objects look like they are always passed by reference, in a transparent and efficient way.
Using PHP5, we can now go back to Example 6.21, except for all the & symbols, all the code still works properly. When we hold a reference in the constructor (line 4), no & symbol is needed. <End of the whole article>