Переход к PHP5 - Функции-события
ОГЛАВЛЕНИЕ
Функция-событие при создании объекта неизвестного класса (__autoload())
PHP не держит все приложение в памяти. Более того, для каждой страницы он заново подгружает все файлы с кодом и преобразует в удобную для выполнения форму. Хорошо помогают различные акселераторы PHP кода, которые сохраняют в памяти непосредственно преобразованный в исполняемый код php-страницу. Но даже в случае использования такого оптимизатора нежелательно подключать к скрипту все файлы с классами и функциями, которые могут понадобится, но реально не используются. Настройка подключение только необходимых классов к каждой конкретной странице - занятие, требующее большой аккуратности и вызывающее большое желание это каким-то образом автоматизировать.
Возможно, именно поэтому и была введенна функция-событие с названием __autoload(), которая срабатывает при попытке обращения к неизвестному классу или интерфейсу. Под обращением понимается попытка создания объекта класса, создание класса-потомка на основе класса, создание класса, реализующего интерфейс.
Еще одна проблема, которую снимает __autoload - это размещение включений файлов в порядке иерархии наследования. Например, если MyClass1 находится в файле MyClass1.php, a MyClass2 - в файле MyClass2.php и MyClass2 extends MyClass1, то с помощью include их надо подключать только в порядке inlude('MyClass1.php'); include('MyClass2.php'); Когда 2 файла - не страшно. Но когда их несколько десятков - это уже сложнее.
И, наконец, пример использования __autoload:
test.php ============================
function __autoload($name) {
include_once('classes/' . $name . '.php');
}
$t = new Textbox();
Control.php =========================
class Control {
// ...
}
Textbox.php =========================
class Textbox extends Control {
// ...
}
При попытке создания Textbox будет загружен файл Textbox.php. Поскольку Textbox extends Control, тут же будет загружен Control.php.
Функции-события при обращении к свойству класса (__get(), __set())
Функции __get и __set могут рассматриваться как возможность реализации свойств, аналогичным свойствам в .NET, VBScript (ASP) или VB. Но в отличие от перечисленных языков (технологий), в PHP __get и __set выполняются для всех (!) свойств. Например:
<b>ASPb>
Class MyClass
Property Let Value(NewValue)
...
End Property
Property Get Value()
...
End Property
Property Let State(NewValue)
...
End Property
Property Get State()
...
End Property
End Class
<b>PHPb>
class MyClass {
function __get($name) {
switch($name) {
case 'Value':
...
break;
case 'State':
...
break;
}
}
function __set($name, $value) {
switch($name) {
case 'Value':
...
break;
case 'State':
...
break;
}
}
}
Вызов методов __get() и __set() при обращении к свойству происходит только если переменной класса с таким именем не существует. Если она сущеструет, то в результате обращения из основной программы можно получить либо ошибку (если переменная private или protected), либо, собственно, переменную (если она public).
Цепочки свойств ($myObj->parent->value) работают корректно. Пример:
class Node {
private $mValue = 1;
private $mParent;
function __get($name) {
switch($name) {
case 'Value':
return $this->mValue;
case 'Parent':
return $this->mParent;
}
}
function __set($name, $value) {
switch($name) {
case 'Value':
$this->mValue = $value;
break;
case 'Parent':
$this->mParent = $value;
break;
}
}
}
$n1 = new Node();
$n2 = new Node();
$n2->Parent = $n1;
$n1->Value = 2;
print $n2->Parent->Value; // Выводит 2.
Функция-событие при обращении к методу класса (__call())
Функция-событие __call(), возможно, введенна вместе с __get() и __set(). Скорее всего эта функция найдет свое применение в дальнейшем. Например, она может применяться для эмуляции перегрузки методов:
class SpecialItem {
//...
}
class GeneralItem {
//...
}
class Processor {
function processSpecialItem(SpecialItem $item) {
//...
}
function processGeneralItem(GeneralItem $item) {
//...
}
function __call($method, $attributes) {
if ($method == 'process' && count($attributes) == 1) {
if ($attributes[0] instanceof GeneralItem)
$this->processGeneralItem($attributes[0]);
elseif ($attributes[0] instanceof SpecialItem)
$this->processSpecialItem($attributes[0]);
}
}
}
$p = new Processor();
$p->process(new GeneralItem()); //processGeneralItem would be called.
$p->process(new SpecialItem()); //processSpecialItem would be called.