Lecture
New object-oriented model in PHP5
When Zeev Suraski added object-oriented (OO) syntax in PHP 3, it could be considered as a “syntactic sugar for accessing collections”. The object-oriented model received inheritance support and allowed the class (and object) to combine methods and properties, but no more. When Ziv and Andi rewrote the PHP 4 engine, it was a completely new engine, working much faster, much more stable and with many more other features. However, the changes practically did not affect the OO model, which was originally introduced in PHP 3.
Although the object model had serious limitations, it was widely used, often in very large applications written in PHP. This triumphant paradigm of the PLO, even so limited in PHP 4, has led to the fact that changes in the object model have become central to the new PHP release, PHP5.
What were the limitations in PHP 3 and 4? The biggest constraint (which led to all the other constraints) was the fact that the semantics of the object instance was the same as for the native types. How did this actually affect the developers? When you assigned a variable (which points to an object) to another variable, a copy of the object was created. Not only did this affect performance, but it also usually caused errors in the application, because many developers thought that both variables would point to the same object. And they pointed to different copies of the same object, therefore, changing one object, we did not change another. Here is an example:
<?php
class Person {
var $name;
function getName() {
return $this->name;
}
function setName($name) {
$this->name = $name;
}
function Person($name) {
$this->setName($name);
}
}
function changeName($person, $name) {
$person->setName($name);
}
$person = new Person("Andi");
changeName($person, "Stig");
print $person->getName();
?>
In PHP 4, this code will output "Andi". The reason is that we pass the $ person object to the changeName () function by value, not by reference, so the $ person object will be copied, and changeName () will already work with a copy of the $ person object.
This behavior is not intuitive. Indeed, many developers expected Java-like behavior. In Java, variables are actually pointers to an object, and therefore, when duplicating, the pointer will be copied, and not the object itself.
There were two kinds of developers: those who knew about this problem, and those who did not. The latter usually did not encounter this problem, because their code was written in such a way that it didn’t matter whether such a problem existed or not. Of course, some of these developers spent sleepless nights in "exciting" searches for "supernatural" bugs. The first group also had a problem, because it was necessary to manually determine the transfer of an object by reference, forbidding the engine to copy objects, and the code was footed with numerous ' & ' signs.
The old object model leads not only to the above problems, but also reveals more fundamental problems that the existing object model did not allow for other possibilities.
In PHP 5, the object model was completely rewritten in order to immediately work with pointers to an object. Unless you explicitly clone an object using the clone keyword, you will never work with a copy of the object, thinking that you are working with the object itself. In PHP 5, you no longer need to explicitly pass objects or assign them by reference, this is done automatically .
Note: explicit passing and assignment by reference is also supported, in case you want to change the contents of a variable or object.
New object-oriented approach in PHP5
New features of the object model are too numerous. Here is an overview of the main changes:
Allows you to control access to methods and properties. Now the visibility of properties and methods can be defined by keywords: public , private , protected . The public modifier allows you to access properties and methods from anywhere. The private modifier allows you to access properties and methods only within the current class. The protected modifier allows accessing the properties and methods of only the current class and a class that inherits the properties and methods of the current class.
<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private
/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// We can redeclare the public and protected method, but not private
protected $protected = 'Protected2';
function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, not Private
?>
PHP 5 allows you to declare constructor methods. Classes in which a constructor method is declared will call this method every time a new object is created, so it may be useful to, for example, initialize any state of the object before using it. A constructor that previously coincided with the class name must now be declared as __construct () , which will make it easier to move classes in hierarchies. Constructors in parent classes are not automatically called. To call a constructor declared in the parent class, call the parent :: __ construct () method.
<?php
class BaseClass {
function __construct() {
print "Конструктор класса BaseClass\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "Конструктор класса SubClass\n";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
If PHP 5 cannot detect the declared __construct () method, the constructor will be called according to the previous scheme, through a call to the method whose name corresponds to the class name. There can be only one compatibility issue for old code if there are classes with __construct () methods.
PHP 5 provides a destructor concept similar to those used in other OO languages, such as Java: when the last object reference is released, the __destruct () method, which takes no parameters, is called before the memory occupied by this object is released .
<?php
class MyDestructableClass {
function __construct() {
print "Конструктор\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Уничтожается " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
?>
As is the case with constructors, destructors declared in the parent class will not be called automatically. To call a destructor declared in a parent class, call the parent :: __ destruct () method.
Creating a copy of an object with absolutely identical properties is not always an acceptable option. For example, when your object contains a link to some other used object and, when you create a copy of the referencing object, you also need to create a new instance of the contained object, so that the copy of the object contains its own separate instance of the contained object.
A copy of the object is created using the clone call (which calls the object's __clone () method, if possible). You can declare a __clone () method that will be called when an object is cloned (after all properties have been copied from the source object).
copy_of_object = clone $object;
When a programmer requests the creation of a copy of an object, PHP 5 will determine whether a __clone () method has been declared for this object or not. If not, the default __clone () method will be called, which will copy all the properties of the object. If the __clone () method has been declared, the creation of copies of the properties in the copy of the object is completely assigned to it. For convenience, the engine provides the programmer with a function that imports all the properties from the source object, so that the programmer can perform meaningful copying of the properties and redefine only the necessary ones. Here is an example of object cloning:
<?php
class MyClass {
function __clone() {
print "Объект был клонирован ";
}
}
$obj = new MyClass();
clone $obj;
?>
Class definitions can now include constants, and reference them using an object. Constants can also be declared within the same class. The difference between variables and constants is that the $ symbol is not used when declaring the last or when referring to them. Like properties and methods, the values of constants declared inside a class cannot be obtained through a variable containing an instance of this class.
<?php
class MyClass {
const SUCCESS = "Success";
const FAILURE = "Failure";
}
print MyClass::SUCCESS;
?>
Class definitions can now include static class members (properties and methods) that are accessed through the class. The general use of static members is shown in an example:
<?php
class Singleton {
static private $instance = NULL;
private function __construct() {
}
static public function getInstance() {
if (self::$instance == NULL) {
self::$instance = new Singleton();
}
return self::$instance;
}
}
?>
You can now define methods as static, allowing them to be called outside the context of the object. Static methods are not defined through the $ this variable, since they should not be limited to a specific object.
<?php
class MyClass {
static function helloWorld() {
print "Hello, world";
}
}
MyClass::helloWorld();
?>
PHP 5 supports the definition of abstract classes and methods. You cannot create an instance of a class that has been declared abstract. A class in which at least one abstract method is declared must also be declared abstract. The methods declared as abstract carry, in essence, only a descriptive meaning and cannot include any functionality. A class can be declared as abstract by using the abstract keyword to exclude the class description from processing. However, you can inherit abstract classes. Practical example:
<?php
abstract class AbstractClass {
/* Данный метод должен быть определён в дочернем классе */
abstract protected function getValue();
/* Общий метод */
public function print() {
print $this->getValue();
}
}
class ConcreteClass1 extends AbstractClass {
protected function getValue() {
return "ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass {
protected function getValue() {
return "ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->print();
$class2 = new ConcreteClass2;
$class2->print();
?>
A method can be declared as abstract , thus deferring its definition by the inherited class. A class that includes abstract methods must be declared as abstract .
<?php
abstract class MyBaseClass {
abstract function display();
}
?>
Function definitions may include an indication of the type of class being passed as a parameter. If the function is called with the wrong type, an error will occur.
<?php
function expectsMyClass(MyClass $obj) {
}
?>
In PHP 4, you could not directly dereference objects that are returned from methods. You would first have to assign such an object to some dummy variable.
Let us explain by example. In PHP 4:
<?php
$dummy = $obj->method();
$dummy->method2();
?>
In PHP 5:
<?php
$obj->method()->method2();
?>
PHP 5 provides an iterator mechanism to list all the properties of an object, for example, for use with the foreach statement . By default, all properties declared as public will participate in the iteration. Example of using iterators:
<?php
class MyClass {
public $var1 = 'value 1';
public $var2 = 'value 2';
public $var3 = 'value 3';
protected $protected = 'protected';
private $private = 'private';
}
$class = new MyClass();
foreach($class as $key => $value) {
print "$key => $value\n";
}
Result:
var1 => value 1 |
As the result shows, foreach has iterated all public properties belonging to the object.
Many developers who write object-oriented applications create a single file that contains the definition of a class. It is very inconvenient to write at the beginning of each script a long list of include files, one for each class.
This is no longer necessary in PHP 5. You can define the __autoload () function, which will automatically be called when using a class that was not defined above. By calling such a function, Zend Engine allows you to load a file with a class definition before an error message is generated and the script stops running.
<?php
function __autoload($class_name) {
include_once($class_name . "php");
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
PHP 5 adds an exception handling paradigm by introducing a try / throw / catch structure. All you need to do is create objects that inherit the Exception exception class.
<?php
class SQLException extends Exception {
public $problem;
function __construct($problem) {
$this->problem = $problem;
}
}
try {
...
throw new SQLException("Couldn't connect to database");
...
} catch (SQLException $e) {
print "Caught an SQLException with problem $obj->problem";
} catch (Exception $e) {
print "Caught unrecognized exception";
}
?>
Currently, for backward compatibility, most internal functions do not use exceptions. However, all new extensions will have this capability, and you can use this construct in your source code. In addition, like the already existing set_error_handler () function, you can use set_exception_handler () to catch an unhandled exception before the script is completed.
In PHP 5, object comparison is a more complex process than in PHP 4, as well as a process that is more in line with the ideology of an object-oriented language.
When using the comparison operator ( == ), the properties of objects are simply compared with each other, namely: two objects are equal if they contain the same properties and their same values and are instances of the same class.
On the other hand, when using the operator of identity ( === ), the properties of an object are considered identical if and only if they refer to the same instance of the same class.
The following example will clarify.
<?php
function bool2str($bool) {
if ($bool === false) {
return 'FALSE';
} else {
return 'TRUE';
}
}
function compareObjects(&$o1, &$o2) {
echo 'o1 == o2 : '.bool2str($o1 == $o2)."\n";
echo 'o1 != o2 : '.bool2str($o1 != $o2)."\n";
echo 'o1 === o2 : '.bool2str($o1 === $o2)."\n";
echo 'o1 !== o2 : '.bool2str($o1 !== $o2)."\n";
}
class Flag {
var $flag;
function Flag($flag=true) {
$this->flag = $flag;
}
}
class OtherFlag {
var $flag;
function OtherFlag($flag=true) {
$this->flag = $flag;
}
}
$o = new Flag();
$p = new Flag();
$q = $o;
$r = new OtherFlag();
echo "Два экземпляра одного и того же класса\n";
compareObjects($o, $p);
echo "\nДве ссылки на один и тот же экземпляр\n";
compareObjects($o, $q);
echo "\nЭкземпляры двух разных классов\n";
compareObjects($o, $r);
?>
The result of this code will be:
Два экземпляра одного и того же класса
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : FALSE
o1 !== o2 : TRUE
Две ссылки на один и тот же экземпляр
o1 == o2 : TRUE
o1 != o2 : FALSE
o1 === o2 : TRUE
o1 !== o2 : FALSE
Экземпляры двух разных классов
o1 == o2 : FALSE
o1 != o2 : TRUE
o1 === o2 : FALSE
o1 !== o2 : TRUE
Accessing an object's properties can be overloaded using the __call , __get, and __set methods . These methods will only work if the object or inherited object does not contain the property to be accessed. The syntax is:
void __set (string name, mixed value)
void __get (mixed name)
By using these methods, references to class properties can be overloaded to execute arbitrary code described in a class. In the name argument, the name of the property being accessed is passed. The argument value of the __set () method must contain the value that will be assigned to the class property named name .
An example overload using __get and __set :
<?php
class Setter {
public $n;
private $x = array("a" => 1, "b" => 2, "c" => 3);
function __get($nm) {
print "Читаем [$nm]\n";
if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Получили: $r\n";
return $r;
} else {
print "Ничего!\n";
}
}
function __set($nm, $val) {
print "Пишем $val в [$nm]\n";
if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Всё плохо!\n";
}
}
}
$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>
The result of the execution of the considered script will be:
Пишем 100 в [a]
OK!
Читаем [a]
Получили: 100
Пишем 101 в [a]
OK!
Читаем [z]
Ничего!
Пишем 1 в [z]
Всё плохо!
object(Setter)#1 (2) {
["n"]=>
int(1)
["x:private"]=>
array(3) {
["a"]=>
int(101)
["b"]=>
int(2)
["c"]=>
int(3)
}
}
Method calls can be overloaded using the __call , __get, and __set methods . These methods will only work if the object or inherited object does not contain the method to which it is being accessed. Syntax:
mixed __call (string name, array arguments)
Using this method, class methods can be overloaded to execute arbitrary code described in a class. In the name argument, the name of the called method is passed. Arguments that were passed to the method during the call will be returned as arguments . The value returned by the __call () method will be passed to the caller.
An example overload using __call :
<?php
class Caller {
private $x = array(1, 2, 3);
function __call($m, $a) {
print "Вызван метод $m :\n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>
The result of the above example:
Вызван метод test:
array(4) {
[0]=>
int(1)
[1]=>
string(1) "2"
[2]=>
float(3.4)
[3]=>
bool(true)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
Object interfaces allow the programmer to create code that indicates which methods and properties the class should include, without the need to describe their functionality.
Interfaces are declared the same as regular classes, but using the keyword " interface "; The interface method bodies must be empty. To include an interface into a class, the programmer must use the " implements " keyword and describe the functionality of the methods listed in the included interface. If required, classes can include more than one interface by listing them separated by spaces.
If a class includes any interface and does not describe the functionality of all the methods of this interface, the execution of code using such a class will end in a fatal error telling which methods have not been described. Interface example:
<?php
interface ITemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
class Template implements ITemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{'.$name.'}', $value, $template);
}
return $template;
}
}
?>
Support for checking dependencies on other objects. The is_a () function, known from PHP 4, is no longer recommended.
<?php
if ($obj instance of Circle) {
print '$obj is a Circle';
}
?>
The final keyword allows you to mark methods so that the inheriting class cannot overload them. By placing a keyword final or declarations of methods or class properties, you can prevent them from being overridden in child classes, for example:
<?php
class BaseClass {
public function test() {
echo "Вызван метод BaseClass::test()\n";
}
final public function moreTesting() {
echo "Вызван метод BaseClass::moreTesting()\n";
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "Вызван метод ChildClass::moreTesting()\n";
}
}
// Выполнение заканчивается фатальной ошибкой:
//Cannot override final method BaseClass::moreTesting()
// (Метод BaseClass::moretesting() не может быть переопределён)
?>
After the class is declared final, it cannot be inherited. The following example will cause an error:
<?php
final class FinalClass {
}
class BogusClass extends FinalClass {
}
?>
For more information about the features of PHP5 and Zend 2.0, you can find out by referring to the documentation of Zend Engine 2.0.
Comments
To leave a comment
Psychology of management
Terms: Psychology of management