- Подробности
-
Категория: PHP. Синтаксис
Александр Аникин
2002-10-25
Данная статья большей частью основана на публикации "Changes in the Zend Engine 2.0", оригинал которой можно найти на www.php.net или www.zend.com.
Данная статья большей частью основана на публикации "Changes in the Zend Engine 2.0", оригинал которой можно найти на www.php.net или www.zend.com. Слабость PHP как объектно-ориентированного языка была очевидна многим профессиональным программистам. Zend Technologies, видимо, тоже озаботилась данной проблемой и выпустила новую версию ядра — Zend Engine 2.0. На основе Zend Engine 2.0 будет выпущена версия PHP 4.3.0 (она доступна уже сейчас на сайте www.php.net в следующей редакции: php-4.3.0-dev-zend2-alpha2). Основные изменения коснулись именно части объектно-ориентированного программирования, а также была добавлена возможность обработки исключений в стиле С++.
Новая объектная модель
Обработка объектов в Ядре Zend 2.0 была полностью изменена для реализации новых возможностей. Объекты интерпретировались в предыдущей версии как основные типы (например, целые и строки). Недостатком этого метода было то, что весь объект копировался, когда определялась переменная или параметры передавались методу. В новой реализации ссылка на объекты осуществляется по дескриптору, а не по значению (можно представить дескриптор, как ID объекта). Возможно, это чем-то похоже на механизм указателей на объекты в С++.
Закрытые члены
Ядро Zend 2.0 позволяет использовать закрытые члены класса. Закрытыми могут быть объявлены только свойства. В случае неправомерного доступа к закрытому члену сообщение об ошибке не генерируется. Член класса является закрытым, если он объявлен с ключевым словом private. Областью видимости закрытого члена является только класс, в котором данный член объявлен. Для производных классов закрытый член не доступен. Объект так же не имеет доступа к закрытому члену. Пример:
<?php
class MyClass {
private $Hello = "Hello, World!n";
function printHello() {
print $this->Hello;
}
}
class MyClass2 extends MyClass {
function printHello() {
MyClass::printHello(); /* Будет напечатоно */
print $this->Hello; /* Не будет напечатано */
}
}
$obj = new MyClass();
print $obj->Hello; /* Не будет напечатано */
$obj->printHello(); /*Будет напечатано */
$obj = new MyClass2();
print $obj->Hello; /* Не будет напечатано */
$obj->printHello();
?>
Клонирование объектов
В ядре Zend 1.0 не существовало возможности использования копирующего конструктора, который вызывался бы при создании дубликата объекта. Во время дублирования ядро Zend 1.0 выполняло побитовое копирование, создавая идентичный дубликат всех свойств объекта. Создание копии объекта с полным дублированием свойств не всегда соответствует ожиданиям. Хорошим примером необходимости использования копирующего конструктора является следующий: если ваш объект содержит ссылку на другой объект, который он использует, то при копировании родительского объекта вы захотите создать новый экземпляр и того объекта, на который ссылается родитель. Копия объекта создается вызовом метода __clone(). Пример: $copy_of_object = $object->__clone(); Когда разработчик создает новую копию объекта (используя выше описанный синтаксис), ядро Zend проверяет, объявлен ли метод __clone() в классе, экземпляром которого является данный объект. Если нет, то будет вызван метод __clone() объявленный по умолчанию, который скопирует все свойства объекта. Если метод __clone() объявлен, то он будет ответственен за установку необходимых свойств при создании объекта. Для удобства, ядро будет поддерживать функцию импортирующую все свойства из исходного объекта. Это может понадобиться для того, чтобы начать копирование с создания дубликата исходного объекта, а затем переопределить лишь необходимые свойства. [Эта функция еще не реализована.] В методе __clone() доступен исходный объект под именем $clone. Пример:
<?php
class MyCloneable {
static $id = 0;
function MyCloneable() {
$this->id = self::$id++;
}
function __clone() {
$this->name = $clone->name;
$this->address = 'New York';
$this->id = self::$id++;
}
}
$obj = new MyCloneable();
$obj->name = 'Hello';
$obj->address = 'Tel-Aviv';
print $obj->id . "n";
$obj = $obj->__clone();
print $obj->id . "n";
print $obj->name . "n";
print $obj->address . "n";
?>
Принудительное вычеркивание объектов
Ядро Zend 1.0 не предоставляло возможности для удаления объекта, если на него существовали ссылки. Теперь можно использовать инструкцию delete, которая вызывает деструктор и уничтожает объект, даже если на этот объект существует ссылка из другого места в ядре. Другие ссылки на уничтоженный объект становятся устаревшими и попытка доступа через них к объекту приведет к фатальной ошибке. Внимание!Если вы объявляли функцию delete() в своих скриптах, то при их обработке анализатор ядра Zend будет генерировать ошибку. Начиная с данной версии ядра "delete" — зарезервированное слово.
Вложенные классы (пространства имен)
Ядро Zend 1.0 обеспечивало только три области видимости: глобальная область видимости, область видимости класса и область видимости функции. Все области, кроме классов могли содержать переменные; только класс и глобальная область могли содержать функции; только глобальная область могла содержать константы и классы. Ядро Zend 2.0 вводит понятие вложенных классов для решения проблемы пересечения имен, делая возможным объявления нескольких таблиц имен, которые могут содержать все типы. Каждый класс может содержать собственный набор констант, функций и статических переменных. Для доступа к локальным именам класса вы можете использовать оператор self:: (например, self::$my_static_name = "Hello"). Вы так же можете использовать имя класса для получения доступа: MyClass::$my_static_name = "Hello". Если вы не указываете в контексте какого класса используются константы и функции, то первоначально осуществляется поиск в текущем классе. Если ничего не найдено, то осуществляется поиск в глобальной области видимости. Если вы хотите заставить PHP проверять только глобальную область, то можете использовать оператор доступа main::. Например, используя main::strlen(), вы можете быть уверены, что вызываете strlen() из глобальной области. Вы должны беспокоится об этом только если объявляете методы с именами, которые уже присутствуют в глобальной области. Для констант вы можете использовать аналогичный синтаксис: self::MY_CONSTANT или main::MY_CONSTANT. Иногда вы не захотите иметь доступ к константам, функциям или классам через оператор доступа к классу (т.е. MyClass::), например из-за частого использования такой конструкции. В этом случае вы можете импортировать функции, классы и константы из классов, используя ключевое слово import. Все это объясняется на примерах ниже. — Классы могут содержать классы. Пример:
<?php
class DB::MySQL {
var $host = '';
function db_connect($user) {
print "Connecting to MySQL database
'$this->host' as $usern";
}
}
class DB::Oracle {
var $host = 'localhost';
function db_connect($user) {
print "Connecting to Oracle database
'$this->host' as $usern";
}
}
$MySQL_obj = new DB::MySQL();
$MySQL_obj->db_connect('Susan');
$Oracle_obj = new DB::Oracle();
$Oracle_obj->db_connect('Barbara');
?>
- Классы могут содержать константы. Пример:
<?php
class foo {
const hey = 'hello';
}
print foo::hey;
?>
- Таблица имен для текущего пространства имен просматривается первой для констант и функций. Пример: Приведенный код печатает "foobar", а не "bar", потому что константа, объявленная в классе, перекрывает глобальную константу с таким же именем.
<?php
define('foo', 'bar');
class FooClass {
const foo = 'foobar';
function printFoo() {
print foo;
}
}
?>
- Класс является текущей областью видимости функции. Пример:
<?php
class FooClass {
function foo() {
$this->bar();
bar();
}
function bar() {
print "foobarn";
}
}
$obj = new FooClass;
$obj->foo();
?>
Данный пример печатает "foobar" два раза, т.к. метод bar() существует в текущем пространстве имен. — Ключевое слово "import" позволяет импортировать имена из одного пространства имен в другое. Пример:
<?php
class MyClass {
class MyClass2 {
function hello() {
print "Hello, World in MyClass2n";
}
}
function hello() {
print "Hello, Worldn";
}
}
import function hello, class MyClass2 from MyClass;
MyClass2::hello();
hello();
?>
Пример:
<?php
class MyOuterClass {
class MyInnerClass {
function func1() {
print "func1()n";
}
function func2() {
print "func2()n";
}
}
}
import class * from MyOuterClass;
import function func2 from MyOuterClass::MyInnerClass;
MyInnerClass::func1();
func2();
?>
Пример:
<?php
class MyOuterClass {
const Hello = "Hello, Worldn";
}
import const Hello from MyOuterClass;
print Hello;
?>
Унификация конструкторов
Ядро Zend позволяет объявлять конструкторы для классов. Классы, которые имеют конструкторы, вызывают его для каждого вновь создаваемого объекта. Конструкторы используются для инициализации объекта перед его использованием. В ядре Zend 1.0 конструктором являлся метод, имеющий такое же имя как и класс. Вызов конструктора базового класса из производного класса мог породить много проблем при изменении иерархии классов. Если для какого-либо класса менялся базовый класс, то при этом необходимо было изменить и имя вызываемого родительского конструктора. Ядро Zend 2.0 вводит общее имя для конструкторов: __construct(). Пример:
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructorn";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructorn";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
Для обратной совместимости реализована следующая возможность: если ядро Zend 2.0 не находит функции __construct(), то оно пытается найти конструктор написанный в старом стиле. Единственный случай, при котором может возникнуть проблема с совместимостью — наличие в вашем старом классе метода __construct(), используемого в другом контексте.
Деструкторы
Очень полезно иметь возможность объявлять деструкторы для объектов. Деструкторы могут выдавать сообщения для отладки, закрывать соединения с БД и выполнять другую работу перед уничтожением объекта. В ядре Zend 1.0 не существовало механизма деструкторов, хотя PHP позволял регистрировать функции, которые вызывались по завершению работы. Ядро Zend 2.0 вводит концепцию деструкторов, схожую с другими объектно-ориентированными языками. Когда последняя ссылка на объект удаляется, вызывается деструктор объекта перед его уничтожением. Деструктор объявляется как метод класса с именем __destruct() и не принимает параметров. Пример:
<?php
class MyDestructableClass {
function __construct() {
print "In constructorn";
$this->name = 'MyDestructableClass';
}
function __destruct() {
print 'Destroying ' . $this->name . "n";
}
}
$obj = new MyDestructableClass();
?>
Как и в случае конструкторов, родительские деструкторы неявно не вызываются. Чтобы вызвать родительский деструктор, вы должны явно определить это в деструкторе: parent::__destruct().
Исключения
Ядро Zend 1.0 не поддерживало обработку исключений. Ядро Zend 2.0 вводит модель исключений, схожую с другими языками программирования (в частности С++). Пример:
<?php
class MyException {
function __construct($exception) {
$this->exception = $exception;
}
function Display() {
print "MyException: $this->exceptionn";
}
}
class MyExceptionFoo extends MyException {
function __construct($exception) {
$this->exception = $exception;
}
function Display() {
print "MyException: $this->exceptionn";
}
}
try {
throw new MyExceptionFoo('Hello');
}
catch (MyException $exception) {
$exception->Display();
}
?>
Если в блоке try инструкцией throw генерируется исключение, то вызывается один из обработчиков catch. В обработчике должен быть указан тип (т.е. имя класса) передаваемого объекта, а также имя, по которому данный объект будет доступен в обработчике. Для одного блока try может быть определено несколько обработчиков. Нужный обработчик будет определен по указанному типу. Только объект может быть передан обработчику. Старый код, не имеющий объявленных пользователем функций 'catch', 'throw' и 'try' будет работать без изменений.
Разыменование объектов, возвращаемых из функций
Пример:
<?php
class Circle {
function draw() {
print "Circlen";
}
}
class Square {
function draw() {
print "Squaren";
}
}
function ShapeFactoryMethod($shape) {
switch ($shape) {
case 'Circle': return new Circle();
case 'Square': return new Square();
}
}
ShapeFactoryMethod('Circle')->draw();
ShapeFactoryMethod('Square')->draw();
?>
Статические переменные, являющиеся членами класса, могут быть инициализированы.
Пример:
<?php
class foo {
static $my_static = 5;
}
print foo::$my_static;
?>
Параметры, передаваемые в функцию по ссылке, могут теперь иметь значение по умолчанию
Пример:
<?php
function my_function(&$var = null) {
if ($var === null) {
die('$var needs to have a value');
}
}
?>