Учитывая, что PHP на сегодняшней день является крайне популярной технологией программирования серверной части сайтов, а также то, что версия PHP 4, по утверждениям многих, была революционной по сравнению с предыдущими, новая пятая по счету версия вызывает обоснованный интерес среди веб-разработчиков.
Однако, по словам создателей PHP 5, ничего революционного в этот раз ожидать не стоит - текущие изменения носят всего лишь ”эволюционный” характер. Тем не менее, сделанные дополнения являются давно ожидаемыми, крайне полезными и весьма своевременными.
В первую очередь переработке подвергся весь механизм работы с объектами. И если в предыдущих версиях объектно-ориентированное программирование на PHP было возможно в минимальной степени, из-за чего и использовалось на практике не часто, то PHP 5 обладает великолепным потенциалом реализации объектного программирования. Кроме этого, PHP обогатился рядом ценных расширений для работы с XML, различными источниками данных, генерации графики и пр.
Основное отличие обработки объектов в PHP 5 от PHP 4 заключается в том, что теперь присвоение объекта или его передача в качестве параметра функции происходит по умолчанию по ссылке, а не по значению, как в предыдущей версии.
И если в PHP 4 объекты обрабатывались также как и простые типы данных, что часто приводило к появлению нескольких копий одного и того же объекта, то в PHP 5 такого не происходит, так как каждый объект получает свой собственный числовой идентификатор (handle), который и используется при обращении к объекту.
Таким образом, представленный ниже код, выполненный в PHP 4 и в PHP 5, очевидно может продемонстрировать различия в обработке объектов.
<?php
class MyClass {
var $property;
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = $obj1;
$obj2->property = 2;
echo $obj1->property; // Выводит 1 в PHP 4 и 2 в PHP 5
echo $obj2->property; // Выводит 2
?>
В PHP 4 $obj2 представляет собой копию объекта $obj1, а в PHP 5 и $obj1 и $obj2 указывают на один и тот же объект, так как оператор $obj2 = $obj1 копирует не сам объект, а только его идентификатор.
Различные механизмы обработки объектов имеют место по причине того, что Zend Engine 1, исполнявший сценарии в PHP 4, хранил значения всех типов одинаковым образом в специальной структуре, называемой zval (Zend VALue). В PHP 5 также используется zval, однако теперь в нем хранятся все типы данных, за исключением объектов, которые располагаются в новой структуре, получившей название Object Store. Zval же хранит только идентификаторы объектов, вот почему при присвоении или передачи в функцию передается не сам объект, а только его идентификатор.
Данное улучшение позволит значительно увеличить производительность PHP сценариев, где активно используется работа с объектами.
Итак, в PHP 5 объекты передаются по ссылке. Однако, если же необходимо провести именно копирование объекта, как это делалось в PHP 4, то в PHP 5 придется явно использовать новый метод __clone(). При этом объект копируется со всеми своими методами, свойствами и их значениями:
<?php
class MyClass{
var $property;
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = clone $obj1;
echo $obj1->property; // Выводит 1
echo $obj2->property; // Выводит 1
$obj2->property = 2;
echo $obj2->property; // Выводит 2
?>
Следует обратить внимание на то, что к методу __clone() нельзя обратиться непосредственно и для копирования объекта используется ключевое слово clone.
Метод __clone() необязательно описывать в самом классе, однако его явное определение, т.е. перегрузка, позволит изменить значения свойств копируемого объекта:
<?php
class MyClass{
var $property;
function __clone() {
$this->property = 2;
}
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = clone $obj1;
echo $obj1->property; // Выводит 1
echo $obj2->property; // Выводит 2
?>
Метод __clone() не может принимать никакие аргументы, однако позволяет обратиться к исходному объекту через указатель $this и получаемому в результате копирования объекту через указатель $that.
В PHP 5 введены спецификаторы доступа public, protected и private, которые позволяют указать степень доступа к свойствам и методам класса.
К общедоступным (public) свойствам и методам можно получить доступ без каких либо ограничений.
Защищенные (protected) элементы класса доступны внутри класса, в котором они объявлены, и в производных от него классах.
Частные (private) элементы доступны только в классе, в котором они объявлены.
<?php
class MyClass {
public $public = "общедоступный элемент";
protected $protected = "защищенный элемент";
private $private = "частный элемент";
public function printPrivate() {
echo $this->private;
}
}
$obj1 = new MyClass;
echo $obj1->public; // Выводит "общедоступный элемент"
class MyClass1 extends myClass {
public function printProtected() {
echo $this->protected;
}
}
$obj2 = new MyClass1();
$obj2->printProtected(); // Выводит "защищенный элемент"
$obj1->printPrivate(); //Выводит "частный элемент"
echo $obj1->protected; // Вызывает ошибку доступа
echo $obj1->private; // Вызывает ошибку доступа
?>
Если не указывать ни один из спецификаторов, то по умолчанию элемент будет иметь уровень доступа public. Такой же уровень доступа получают свойства, для объявления которых использовалось устаревшее и не рекомендуемое к использованию в PHP 5 ключевое слово var.
Аналогично, при уничтожении объекта вызывается специальный метод __destruct() – деструктор класса.
<?php
class MyClass {
function __construct() {
echo "Запущен конструктор";
}
function __destruct() {
echo "Запущен деструктор";
}
}
$obj = new MyClass(); // Выводит "Запущен конструктор"
unset($obj); // Выводит "Запущен деструктор"
?>
Если же необходимо вызвать конструктор или деструктор базового класса, то необходимо это делать явно, через указатель parent.
<?php
class MyClass {
function __construct() {
echo "Запущен конструктор базового класса";
}
function __destruct() {
echo "Запущен деструктор базового класса";
}
}
class MyClass1 extends MyClass {
function __construct() {
parent::__construct();
}
function __destruct() {
parent::__destruct();
}
}
$obj = new MyClass1(); // Выводит "Запущен конструктор
// базового класса"
unset($obj); // Выводит "Запущен деструктор базового класса"
?>
Для целей совместимости с предыдущей версией PHP 5 поступает следующем образом: если при создании объекта в классе не найдет конструктор __construct(), то PHP пытается выполнить метод, имя которого совпадает с именем класса. Т.о. конструкторы PHP 4 будут работать с PHP 5 без каких-либо изменений кода.
<?php
abstract class MyClass {
abstract public function abstrFunc();
}
class MyClass1 extends MyClass {
public function abstrFunc() {
echo 1;
}
}
$obj = new MyClass1;
$obj->abstrFunc(); // Выводит 1
?>
При этом невозможно создать объект абстрактного класса, можно только определять новые классы от базового абстрактного класса и создавать объекты уже от производных классов.
Стоит отметить, что абстрактные классы также могут содержать и обычные (не абстрактные) элементы.
Основное отличие интерфейсов от абстрактных классов заключается в том, что в PHP 5 класс не может быть порожден от нескольких классов, в том числе и абстрактных, но зато может быть создан на основе любого числа интерфейсов.
При этом в интерфейсе методы объявляются ключевым словом function без указания каких-либо спецификаторов, в том числе и abstract.
<?php
interface Int1 {
function func1();
}
interface Int2 {
function func2();
}
class MyClass implements Int1, Int2 {
public function func1() {
echo 1;
}
public function func2() {
echo 2;
}
}
$obj = new MyClass;
$obj->func1(); // Выводит 1
$obj->func2(); // Выводит 2
?>
Таким образом, хотя множественное наследование (multiple inheritance) и не поддерживается в PHP 5, однако разработчики получили реальную возможность создавать классы на основе отдельно описанных интерфейсов.
Метод, при определении которого используется ключевое слово final, не может быть переопределен в классах, производных от данного класса.
<?php
class MyClass {
final public function func() {
// Код метода
}
}
class MyClass1 extends MyClass {
// Следующий код вызывает ошибку
// переопределения финального метода
// базового класса MyClass
public function func() {
// Код метода
}
}
?>
Кроме этого, если final используется при определении самого класса, то порождение от него других классов становится невозможным.
<?php
final class MyClass {
// Код описания класса
}
// Следующий код вызывает ошибку
// порождения от финального класса
class MyClass1 extends MyClass {
// Код описания класса
}
?>
Если класс определен как final, то и все методы данного класса автоматически становятся финальными, таким образом, определять их явно как final уже нет необходимости.
Определять же свойства класса как финальные – недопустимо.
<?php
class MyClass {
const CONSTANT = "константа класса";
}
echo MyClass::CONSTANT; // Выводит "константа класса"
?>
Ранее написанные сценарии для PHP 4, не использующие функции или классы с именем const будут работать без модификации кода.
<?php
class MyClass {
static $static = 1;
}
echo MyClass::$static; // Выводит 1
?>
Статические свойства едины для всего класса и не могут принадлежать ни одному из объектов класса. Изменение такого свойства в одном из методов любого объекта приводит к его изменению для всех остальных объектов данного класса. Кроме этого, становится возможным обратиться к такому свойству вне контекста объекта.
Статические методы классов в PHP 5, также как и статические свойства, принадлежат всему классу в целом. Это позволяет использовать такой метод без создания какого-либо объекта такого класса.
<?php
class MyClass {
static function statFunc() {
echo "статический метод";
}
}
MyClass::statFunc(); // Выводит "статический метод"
?>
Однако в статическом методе становится невозможным использовать указатель $this, так как при вызове статического метода или неизвестно в контексте какого объекта он вызывается, или такого объекта может и не существовать вовсе.
<?php
class MyClass { }
$obj1 = new MyClass();
if ($obj1 instanceof MyClass) {
echo "\$obj1 - объект класса MyClass";
}
class MyClass1 extends MyClass { }
$obj2 = new MyClass1();
if ($obj2 instanceof MyClass) {
echo "\$obj2 - объект класса, производного от MyClass";
}
interface Int { }
class MyClass2 implements Int { }
$obj3 = new MyClass2();
if ($obj3 instanceof Int) {
echo "\$obj3 - объект класса, созданного на основе интерфейса Int";
}
?>
Также с помощью instanceof можно определить является ли объект экземпляром класса, созданного на основе определенного интерфейса.
<?php
function __autoload($class) {
echo "попытка создать объект неопределенного класса ", $class;
}
$obj = new MyClass;
?>
В качестве параметра функции __autoload() передается имя неопределенного класса.
<?php
class MyClass {
private $properties;
function __set($name, $value) {
echo "задание нового свойства $name = $value";
$this->properties[$name]=$value;
}
function __get($name) {
echo "чтение значения свойства ", $name;
return $this->properties[$name];
}
}
$obj = new MyClass;
$obj->property = 1; // Выводит "задание нового свойства property=1"
$a = $obj->property; // Выводит "чтение значения свойства property"
echo $a; // выводит 1;
?>
Новые методы доступа __get() и __set() позволяют легко проводить динамическое назначение свойств объектам. В качестве параметров этим методам передаются имена свойств.
При вызове в PHP 5 несуществующего метода объекта автоматически вызывается специальный метод __call().
<?php
class MyClass {
function __call($name, $params) {
echo "Вызван метод $name с параметром $params[0]";
}
}
$obj = new MyClass;
echo $obj->method(1); // Выводит "Вызван метод method
// с параметром 1"
?>
В качестве параметров __call() принимает имя вызываемого метода и передаваемые этому методу параметры.
<?php
class MyClass {
public $a = 1;
public $b = 2;
}
$obj = new MyClass;
foreach ($obj as $name => $value) {
echo "$name => $value "; // Выводит "a => 1 b => 2"
}
?>
Специальные интерфейсы PHP 5 IteratorAggregate и Iterator позволяют указывать поведение класса при его использовании с оператором foreach.
Однако, по словам создателей PHP 5, ничего революционного в этот раз ожидать не стоит - текущие изменения носят всего лишь ”эволюционный” характер. Тем не менее, сделанные дополнения являются давно ожидаемыми, крайне полезными и весьма своевременными.
В первую очередь переработке подвергся весь механизм работы с объектами. И если в предыдущих версиях объектно-ориентированное программирование на PHP было возможно в минимальной степени, из-за чего и использовалось на практике не часто, то PHP 5 обладает великолепным потенциалом реализации объектного программирования. Кроме этого, PHP обогатился рядом ценных расширений для работы с XML, различными источниками данных, генерации графики и пр.
Новый механизм работы с объектами
Основное отличие обработки объектов в PHP 5 от PHP 4 заключается в том, что теперь присвоение объекта или его передача в качестве параметра функции происходит по умолчанию по ссылке, а не по значению, как в предыдущей версии. И если в PHP 4 объекты обрабатывались также как и простые типы данных, что часто приводило к появлению нескольких копий одного и того же объекта, то в PHP 5 такого не происходит, так как каждый объект получает свой собственный числовой идентификатор (handle), который и используется при обращении к объекту.
Таким образом, представленный ниже код, выполненный в PHP 4 и в PHP 5, очевидно может продемонстрировать различия в обработке объектов.
<?php
class MyClass {
var $property;
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = $obj1;
$obj2->property = 2;
echo $obj1->property; // Выводит 1 в PHP 4 и 2 в PHP 5
echo $obj2->property; // Выводит 2
?>
В PHP 4 $obj2 представляет собой копию объекта $obj1, а в PHP 5 и $obj1 и $obj2 указывают на один и тот же объект, так как оператор $obj2 = $obj1 копирует не сам объект, а только его идентификатор.
Различные механизмы обработки объектов имеют место по причине того, что Zend Engine 1, исполнявший сценарии в PHP 4, хранил значения всех типов одинаковым образом в специальной структуре, называемой zval (Zend VALue). В PHP 5 также используется zval, однако теперь в нем хранятся все типы данных, за исключением объектов, которые располагаются в новой структуре, получившей название Object Store. Zval же хранит только идентификаторы объектов, вот почему при присвоении или передачи в функцию передается не сам объект, а только его идентификатор.
Данное улучшение позволит значительно увеличить производительность PHP сценариев, где активно используется работа с объектами.
Клонирование объектов
Итак, в PHP 5 объекты передаются по ссылке. Однако, если же необходимо провести именно копирование объекта, как это делалось в PHP 4, то в PHP 5 придется явно использовать новый метод __clone(). При этом объект копируется со всеми своими методами, свойствами и их значениями:<?php
class MyClass{
var $property;
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = clone $obj1;
echo $obj1->property; // Выводит 1
echo $obj2->property; // Выводит 1
$obj2->property = 2;
echo $obj2->property; // Выводит 2
?>
Следует обратить внимание на то, что к методу __clone() нельзя обратиться непосредственно и для копирования объекта используется ключевое слово clone.
Метод __clone() необязательно описывать в самом классе, однако его явное определение, т.е. перегрузка, позволит изменить значения свойств копируемого объекта:
<?php
class MyClass{
var $property;
function __clone() {
$this->property = 2;
}
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = clone $obj1;
echo $obj1->property; // Выводит 1
echo $obj2->property; // Выводит 2
?>
Метод __clone() не может принимать никакие аргументы, однако позволяет обратиться к исходному объекту через указатель $this и получаемому в результате копирования объекту через указатель $that.
Доступность элементов класса
В PHP 5 введены спецификаторы доступа public, protected и private, которые позволяют указать степень доступа к свойствам и методам класса. К общедоступным (public) свойствам и методам можно получить доступ без каких либо ограничений.
Защищенные (protected) элементы класса доступны внутри класса, в котором они объявлены, и в производных от него классах.
Частные (private) элементы доступны только в классе, в котором они объявлены.
<?php
class MyClass {
public $public = "общедоступный элемент";
protected $protected = "защищенный элемент";
private $private = "частный элемент";
public function printPrivate() {
echo $this->private;
}
}
$obj1 = new MyClass;
echo $obj1->public; // Выводит "общедоступный элемент"
class MyClass1 extends myClass {
public function printProtected() {
echo $this->protected;
}
}
$obj2 = new MyClass1();
$obj2->printProtected(); // Выводит "защищенный элемент"
$obj1->printPrivate(); //Выводит "частный элемент"
echo $obj1->protected; // Вызывает ошибку доступа
echo $obj1->private; // Вызывает ошибку доступа
?>
Если не указывать ни один из спецификаторов, то по умолчанию элемент будет иметь уровень доступа public. Такой же уровень доступа получают свойства, для объявления которых использовалось устаревшее и не рекомендуемое к использованию в PHP 5 ключевое слово var.
В PHP 5 введены конструкторы и деструкторы
Метод-конструктор вызывается автоматически при каждом создании объекта. И хотя конструктор появился в PHP давно (эту роль выполнял метод, названный именем класса), но в PHP 5 была изменена схема именования конструктора - метод __construct() является теперь конструктором класса.Аналогично, при уничтожении объекта вызывается специальный метод __destruct() – деструктор класса.
<?php
class MyClass {
function __construct() {
echo "Запущен конструктор";
}
function __destruct() {
echo "Запущен деструктор";
}
}
$obj = new MyClass(); // Выводит "Запущен конструктор"
unset($obj); // Выводит "Запущен деструктор"
?>
Если же необходимо вызвать конструктор или деструктор базового класса, то необходимо это делать явно, через указатель parent.
<?php
class MyClass {
function __construct() {
echo "Запущен конструктор базового класса";
}
function __destruct() {
echo "Запущен деструктор базового класса";
}
}
class MyClass1 extends MyClass {
function __construct() {
parent::__construct();
}
function __destruct() {
parent::__destruct();
}
}
$obj = new MyClass1(); // Выводит "Запущен конструктор
// базового класса"
unset($obj); // Выводит "Запущен деструктор базового класса"
?>
Для целей совместимости с предыдущей версией PHP 5 поступает следующем образом: если при создании объекта в классе не найдет конструктор __construct(), то PHP пытается выполнить метод, имя которого совпадает с именем класса. Т.о. конструкторы PHP 4 будут работать с PHP 5 без каких-либо изменений кода.
В PHP 5 впервые введены абстрактные (abstract) классы и методы
Абстрактные методы имеют только объявление и не имеют реализации. Класс, который содержит такие методы, должен быть обязательно объявлен как абстрактный.<?php
abstract class MyClass {
abstract public function abstrFunc();
}
class MyClass1 extends MyClass {
public function abstrFunc() {
echo 1;
}
}
$obj = new MyClass1;
$obj->abstrFunc(); // Выводит 1
?>
При этом невозможно создать объект абстрактного класса, можно только определять новые классы от базового абстрактного класса и создавать объекты уже от производных классов.
Стоит отметить, что абстрактные классы также могут содержать и обычные (не абстрактные) элементы.
Итнерфейсы
Интерфейсами (interface) являются абстрактные классы, содержащие только абстрактные методы и не имеющие никаких свойств.Основное отличие интерфейсов от абстрактных классов заключается в том, что в PHP 5 класс не может быть порожден от нескольких классов, в том числе и абстрактных, но зато может быть создан на основе любого числа интерфейсов.
При этом в интерфейсе методы объявляются ключевым словом function без указания каких-либо спецификаторов, в том числе и abstract.
<?php
interface Int1 {
function func1();
}
interface Int2 {
function func2();
}
class MyClass implements Int1, Int2 {
public function func1() {
echo 1;
}
public function func2() {
echo 2;
}
}
$obj = new MyClass;
$obj->func1(); // Выводит 1
$obj->func2(); // Выводит 2
?>
Таким образом, хотя множественное наследование (multiple inheritance) и не поддерживается в PHP 5, однако разработчики получили реальную возможность создавать классы на основе отдельно описанных интерфейсов.
Финальные методы и классы
В PHP 5 введена новая возможность определять методы класса и сами классы как финальные (final).Метод, при определении которого используется ключевое слово final, не может быть переопределен в классах, производных от данного класса.
<?php
class MyClass {
final public function func() {
// Код метода
}
}
class MyClass1 extends MyClass {
// Следующий код вызывает ошибку
// переопределения финального метода
// базового класса MyClass
public function func() {
// Код метода
}
}
?>
Кроме этого, если final используется при определении самого класса, то порождение от него других классов становится невозможным.
<?php
final class MyClass {
// Код описания класса
}
// Следующий код вызывает ошибку
// порождения от финального класса
class MyClass1 extends MyClass {
// Код описания класса
}
?>
Если класс определен как final, то и все методы данного класса автоматически становятся финальными, таким образом, определять их явно как final уже нет необходимости.
Определять же свойства класса как финальные – недопустимо.
Константы класса
В PHP 5 введен новый элемент класса – константа.<?php
class MyClass {
const CONSTANT = "константа класса";
}
echo MyClass::CONSTANT; // Выводит "константа класса"
?>
Ранее написанные сценарии для PHP 4, не использующие функции или классы с именем const будут работать без модификации кода.
Статические свойства и методы класса
В PHP 5 возможно объявление статических свойств класса.<?php
class MyClass {
static $static = 1;
}
echo MyClass::$static; // Выводит 1
?>
Статические свойства едины для всего класса и не могут принадлежать ни одному из объектов класса. Изменение такого свойства в одном из методов любого объекта приводит к его изменению для всех остальных объектов данного класса. Кроме этого, становится возможным обратиться к такому свойству вне контекста объекта.
Статические методы классов в PHP 5, также как и статические свойства, принадлежат всему классу в целом. Это позволяет использовать такой метод без создания какого-либо объекта такого класса.
<?php
class MyClass {
static function statFunc() {
echo "статический метод";
}
}
MyClass::statFunc(); // Выводит "статический метод"
?>
Однако в статическом методе становится невозможным использовать указатель $this, так как при вызове статического метода или неизвестно в контексте какого объекта он вызывается, или такого объекта может и не существовать вовсе.
Instanseof
Специальное ключевое слово instanceof в PHP 5 позволяет определять является ли объект экземпляром определенного класса, или же экземпляром класса производного от какого-либо класса.<?php
class MyClass { }
$obj1 = new MyClass();
if ($obj1 instanceof MyClass) {
echo "\$obj1 - объект класса MyClass";
}
class MyClass1 extends MyClass { }
$obj2 = new MyClass1();
if ($obj2 instanceof MyClass) {
echo "\$obj2 - объект класса, производного от MyClass";
}
interface Int { }
class MyClass2 implements Int { }
$obj3 = new MyClass2();
if ($obj3 instanceof Int) {
echo "\$obj3 - объект класса, созданного на основе интерфейса Int";
}
?>
Также с помощью instanceof можно определить является ли объект экземпляром класса, созданного на основе определенного интерфейса.
__autoload()
Специальная функция __autoload() будет вызываться в PHP 5 в случае попытки создания объекта неопределенного класса.<?php
function __autoload($class) {
echo "попытка создать объект неопределенного класса ", $class;
}
$obj = new MyClass;
?>
В качестве параметра функции __autoload() передается имя неопределенного класса.
Методы доступа к свойствам объекта
В PHP 5 возможна перегрузка доступа к свойствам объектов.<?php
class MyClass {
private $properties;
function __set($name, $value) {
echo "задание нового свойства $name = $value";
$this->properties[$name]=$value;
}
function __get($name) {
echo "чтение значения свойства ", $name;
return $this->properties[$name];
}
}
$obj = new MyClass;
$obj->property = 1; // Выводит "задание нового свойства property=1"
$a = $obj->property; // Выводит "чтение значения свойства property"
echo $a; // выводит 1;
?>
Новые методы доступа __get() и __set() позволяют легко проводить динамическое назначение свойств объектам. В качестве параметров этим методам передаются имена свойств.
Перегрузка вызовов методов объекта
Метод __set() также получает и значение, которое устанавливается для свойства.При вызове в PHP 5 несуществующего метода объекта автоматически вызывается специальный метод __call().
<?php
class MyClass {
function __call($name, $params) {
echo "Вызван метод $name с параметром $params[0]";
}
}
$obj = new MyClass;
echo $obj->method(1); // Выводит "Вызван метод method
// с параметром 1"
?>
В качестве параметров __call() принимает имя вызываемого метода и передаваемые этому методу параметры.
Итераторы
В PHP 5 возможен полный перебор всех свойств объекта в операторе foreach.<?php
class MyClass {
public $a = 1;
public $b = 2;
}
$obj = new MyClass;
foreach ($obj as $name => $value) {
echo "$name => $value "; // Выводит "a => 1 b => 2"
}
?>
Специальные интерфейсы PHP 5 IteratorAggregate и Iterator позволяют указывать поведение класса при его использовании с оператором foreach.