Профессиональная разработка Web-приложений.  
Боишься нашего дизайна?
Новости
PDF журнал
Участники проектa
Сотрудничество
Ссылки
Карта сайта
Комментарии
Комментарии к статье
Добавить комментарий
Обсудить на форуме
Информация об авторе
Оценка статьи

Переход к PHP5

Оригинал статьи доступен по адресу http://devlink.crimea.ua/articles/article.php?article_id=6

Краткий обзор возможностей PHP по материалам PHP 5 beta 3.

Оглавление

Введение

PHP, фактически, является самым распостраненным языком для Web программирования. В достижении этого статуса он прошел множество этапов, от простого языка для Web программирования который уступал многим (php3), до сегодняшнего быстрого мощного и расширяемого (PHP4). Тем более приятно, что PHP не стоит на месте и продолжает развиваться оставаясь таким-же простым для начинающих и предоставляя все больше возможностей более квалифицированным разработчикам. В предверьи выхода PHP5 появляется множество информации о улучшениях в PHP 5, включая информацию от самих разработчиков. Например, замечательная статья от одного из авторов Zend Engine - Zeev Suraski (http://phpclub.ru/detail/article/2004-01-07) и её перевод на phpclub.ru очень помогут в понимании тонкостей изменения работы с объектами. Существуют, конечно, и общие обзоры возможностей PHP5, сделанные как отечественными так и зарубежными авторами. В своем обзоре я попытался дать наиболее полное представление о изменениях в PHP5, на сколько это возможно. Эта статья также является в некотором роде "отчетом о проделанной работе", поскольку обзор каждого изменения собровождался детальным его изучением и попыткой использовать на практике.

Хочу также выразить благодарность людям, внесшим вклад в улучшение этой статьи и обсуждавшим её на форуме phpclub.ru.

На этом заканчиваем введение и приступаем к основной части.

Изменения в PHP5

Новые уровни доступа private и public

В PHP5 добавлены новые модификаторы уровня доступа для переменных классов. Как и во многих других языках программирования, они носят названия private, protected и public.

Private - самый ограниченничивающий модификатор. Private переменная класса может быть использованна только в классе, в котором объявленна. К ней невозможно обратиться из другого программного кода.

Protected - расширение области private, добавляющее к ней возможность обращаться к переменной из классов-потомков.

Public - расширяющий protected модификатор, задающий наиболее широкую область доступа. К возможности использовать переменную в классах-потомках, добавляется возможность обращаться к переменной непосредственно из другого кода. Строго говоря, public не является новой областью доступа. Ранее в PHP все переменные классов являлись public переменными.

Private переменные используются для алгоритмов, которые используются только в текущем классе и не могут быть переопределенны в классах-потомках. Protected может быть использован, когда организовывается семейство объектов, обладающих сходными алгоритмами и организованных в иерархию. Использование public переменных, обычно, не является хорошей практикой но иногда оправданно. Их можно использовать, если у класса много свойств, которые должны быть доступны всем алгоритмам, использующим этот класс.

Аналогично, private/protected/public мидификаторы применяются к методам класса. Методы, объявленные без модификатора, являются public методами.

Если метод или переменная переопределяются в классе-наследнике, то уровень доступа должен быть таким-же или выше. Например, protected метод в классе-потомке можно сделать public, но нельзя private.

Для примера рассмотрим классы NewClass и NewClass1.

 class NewClass {
    
// new PHP5 modifiers
    
private $myPrivateVar 'myPrivateVar';
    protected 
$myProtectedVar 'myProtectedVar';
    public 
$myPublicVar 'myPublicVar';
    
// old PHP declaration
    
var $myVar 'myVar';
}

class 
NewClass1 extends NewClass {
    function 
getProtectedVar() {
        return 
$this->myProtectedVar;
    }

NewClass содержит несколько переменных с разными областями доступа. NewClass1 используется для тестирования областей видимости, связанных с наследованием.

Создаем объекты классов:

 $c = new NewClass();
$c1 = new NewClass1(); 

Обращаемся к переменным:
print $c->myPrivateVar;
    Непосредственное обращение к private переменной приводит к ошибке.
print $c->myProtectedVar;
    Непосредственное обращение к protected переменной приводит к ошибке.
print $c->myPublicVar;
    Обращение к public переменной возвращает её значение.
print $c->myVar;
    Обращение к переменной, объявленной в старом стиле, равносильно обращению к public переменной.
print $c1->myPrivateVar;
    Private переменная не была унаследованна классом NewClass1. Обращение к ней равнозначно обращению к необъявленной переменной.
print $c1->myProtectedVar;
    Protected переменная была унаследованна и непосредственное обращение к ней приводит к ошибке. Для проверки, что она была унаследованна вместе с начальным значением, можно вызвать "print $c1->getProtectedVar();".
print $c1->myPublicVar;
    Public переменная была унаследованна и обращение к ней возвращает её значение.

Абстрактные классы и методы (abstract)

Абстрактные классы используются для создания семейства объектов, обладающих единым интерфейсом. Также они используются, когда нужно запретить создание объекта некоторого класса.

Пример создания и использования абстрактного класса:

 abstract class NewClass {
    abstract function 
myMethod();
}

class 
NewClass1 extends NewClass {
    function 
myMethod() {
        return 
'myMethod';
    }
}

$c = new NewClass1();
print 
$c->myMethod(); 

Если метод определяется как abstract, он должен быть переопределен в классе-потомке. При этом параметры переопределенного метода должны совпадать с параметрами абстрактного метода. Модификатор уровня доступа для абстрактных методов не учитывается. Уровень доступа определяется методом, переопределяющим абстрактный.

Интерфейсы (interface)

Интерфейс похож на абстрактный класс, за исключением того, что использование интерфейсов позволяет использовать множественное наследование. Таким образом, класс может реализовывать несколько интерфейсов одновременно, а не расширять только один абстрактный класс.

Пример использования интерфейса:

 interface Printable {
    public function 
dump();
}
interface 
Editable {
    public function 
edit();
}

class 
NewClass implements PrintableEditable {
    function 
dump() { }
    function 
edit() { }
}

$c = new NewClass();
print ((
$c instanceof Printable) ? 'true' 'false'); 

Типизация пареметров функций на уровне классов

Для параметров функций можно задавать класс, объект которого может быть передан по этому параметру. Во время работы скрипта, конструкция

 function myFunction(MyClass $obj) {

равнозначна конструкции

 function myFunction($obj) {
    if (!(
$obj instanceof MyClass || $obj == null)) {
        die(
'Argument 1 must be an instance of ClassName');
    }

При этом instanceof распостраняется не только на имя класса, но и на всех его предков и реализуемые интерфейсы.

Например, следующий код выполнится без ошибок:

 interface Editable {
    function 
edit();
}

abstract class 
View {
    abstract function 
createView();
}

class 
NewClass extends View implements Editable {
    function 
createView() { }
    function 
edit() { }
    function 
createMyView(View $obj) { }
    function 
doEdit(Editable $obj) { }
}

$c = new NewClass();
$c->createMyView($c);
$c->doEdit($c); 

Финальные классы и методы (final)

Финальный метод невозможно переопределить в классе-наследнике. Финальный класс невозможно использовать для создания классов-наследников. Это может пригодиться, когда необходимо сохранить алгоритм, инкапсулированный в классе, неизменным. Например, что бы ограничить программиста, использующего библиотеку, от переопределения поведения. Использование финальных классов вместе с типизацией параметров функций создает практически 100% препятствие на пути расширения или подмены функциональности. Естественно, при открытом исходном коде убрать final у класса или метода не является трудной задачей, но, например, final часто используется у классов, определенных в самом PHP или его расширениях (Exception class, DOM extention).

Пример финального класса и финального метода:

 final class Security {
    function 
createUser() {
        ...
    }
}

class 
View {
    final static function 
createView(Security $user) {
        ...
    }

Поскольку класс Security является финальным, а параметром функции View::createView может быть только объект финального класса или null, это дает 100% гарантию, что в если в функцию createView будет передан объект, то это будет только объект класса Security, а не подмененный.

Клонирование объектов

В PHP4 для клонирования объекта достаточно было простой операции $clonedObject = $object. Все свойства обекта $object просто копировались в объект $clonedObject. Изменить алгоритм клоирования можно было написав собственный метод для этого. В PHP5 для этого метода ввели специальное имя __clone и упростили доступ к созданному объекту. Для обращения к новому объекту используется $this, для обращения к уже существующему (чей клон делается), соответственно, $that.

Если метода __clone нет, то вызовется стандартный метод, копирующий все свойства объекта.

На примере это выглядит так:

 class Node {
    private 
$next;
    private 
$name;

    function 
__clone() {
        
$this->name $that->name;
        
$this->next null;
    }

    function 
setName($name) { $this->name $name; }
    function 
getName() { return $this->name; }
    function 
setNext(Node $next) { $this->next $next; }
}

$n1 = new Node();
$n1->setName('Node1');

$n2 = new Node();
$n2->setName('Node2');
$n1->setNext($n2);

$n $n2->__clone();
print_r($n); 

В примере рассматривается класс для создания списка, т.е. цепочки объектов, в которой каждый объект сожержит указатель на следующий. При этом можно получить клон любого объекта в цепочке, и новый объект будет "вынутым" из цепочки (не содержать ссылки на следующий объект).

Пример также демонстрирует, что к можно внутри метода __clone можно получить доступ к private переменным объектов $this и $that.

Конструкторы

Основным недостатком структуры конструкторов в PHP4 является необходимость синхронизации имени конструктора и имени класса. Поскольку имя конструктора должно совпадать с именем класса, то, при изменении имени класса, приходится переименовывать и конструкторы. В случае, если класс имеет несколько наследников, приходится аккуратно изменять в классах наследниках наследуемый класс (extends) и вызов конструктора класса-предка (parent).

Введение в PHP5 конструктора для класса с общим именем __construct упрощает переименовывание классов во время их разработки. Если в классе есть и __construct и функция, имя которой совпадает с именем класса, то в качестве конструктора будет вызванно __construct. При перегрузке метода-конструтора вызов конструктора класса-предка осуществляется через parent::__construct().

Пример использования конструкторов:

 class NewClass1 {
    function 
__construct() {
        print 
'NewClass1::__construct called';
    }
}

class 
NewClass2 extends NewClass1 {
}

class 
NewClass3 extends newClass2 {
    function 
__construct() {
        print 
'NewClass3::__construct called';
        
parent::__construct();
    }
}

$n1 = new NewClass1();
// выводится NewClass1::__construct called

$n2 = new NewClass2();
// выводится NewClass1::__construct called - конструктор унаследован и вызван

$n3 = new NewClass3();
// выводится NewClass3::__construct called и NewClass1::__construct called 

При этом, если конструктор объявлен с модификатором private, то класс с таким конструктором создать невозможно. Однако обращение parent::__construct возможно. Это дает еще один способ избежать создания класса, помимо объявления его abstract.

Деструкторы

Деструкторы являются нововведением для PHP. Они очень полезны для совершения работы по освобождению ресурсов, таких как закрытие открытых файлов или соединения с базой данных. Для деструкторов определенно имя __destruct. Как и для конструкторов, если деструктор унаследован и не перегружен он вызовется. Если он перегружен, то вызовется только перегруженный конструктор. Для вызова деструктора объекта-предка надо использовать parent::__destruct(). Деструктор вызывается без параметров.

Пример использования деструктора:

 class Computer {
    function 
compute() {
        
// большие ресурсоемкие вычисления.
    
}

    function 
__destruct() {
        
// отправить письмо, что все выполнилось
    
}
}

$c = new Computer();
$c->compute(); 

Константы

В классах могут быть объявленны константы. Это является еще одним методом (вместе с final классами и методами) для повышения структурности и удобочитаемости кода.

Пример определения и использования констант:

 final class ControlTypes {
    const 
Textbox 1;
    const 
Label 2;
    const 
Listbox 3;
    const 
Textarea 4;
    const 
Link 7;
    const 
Button 6;
}

class 
Control {
    private 
$type;

    function 
__construct($type) {
        
$this->type $type;
    }
}

$c = new Control(ControlTypes::Textbox); 

К константам невозможно применять модификаторы public, protected, private. Константы всегда public. Обращаться к константам можно только через имя класса, например ControlType::Textbox. Обращения через $this или другой указатель на объект класса не поддерживаются. В константе может быть только значение примитивного типа, т.е. строка или число. Константы наследуются и могут быть переопределены в классах-потомках.

Интересной особенностью является то, что интерфейсы могут содержать константы. Например:

 interface myInterface {
    const 
test 2;

Система перехвата исключений (exceptions)

Exceptions (исключения) - это неотъемлемая часть любого современного языка. Система перехвата исключений объединяет в себе оператор throw, стрктуру языка "try { .. } catch ()[ catch () ...]" и основной объект Exception. В отличии от Java exceptions, в PHP отсутствует завершающий блок finally.

Основное применение системы исключений состоит в использовании структуры try/catch для отделения основного кода программы и блоков обработки ошибок. Механизм exceptions позволяет также корректно обрабатывать исключения, возникшие не непосредственно в выполняемом коде, а в используемых функциях.

Следующий пример демонстрирует отделение кода от обработчиков нестандартных ситуаций:

 /**
* Замечания:
*   Конструктор DatabaseConnection может бросить DatabaseException
*   Метод getUser() может бросить UserNotFoundException
*   Метод sendMail() может бросить MailServiceException
*/
try {
  
$cn = new DatabaseConnection();
  
$admin cn->getUser('Admin');
  
$admin->sendMail('Database check is complete');
} catch (
DatabaseException $e) {
  print 
"Невозможно создать соединение с базой данных. Причина: " $e->getMessage();
} catch (
UserNotFoundException $e) {
  print 
"Пользователя не существует";
} catch (
MailServiceException $e) {
  print 
"Ошибка отправки письма: " $e->getMessage();
} catch (
Exception $e) {
  print 
"Общая ошибка: " $e->getMessage();

В общем случае, использование системы исключений можно заменить на использование структур if и goto или только if, но код программы в результате становиться значительно более громоздким.

Система исключений в PHP работает только с исключениями, "бросаемыми" оператором throw. Ошибки синтаксиса языка не обрабатываются блоками try/catch по очевидным причинам.

В PHP на данный момент определен только один класс исключений: Exception. Для более гибкой работы с системой сообщений можно добавлять свои классы исключений но наследовать их от базового класса Exception, что бы всегда можно было поймать исключение (catch exception).

Основными методами класса Exception являются: getMessage(), getCode(), getTrace(), getFile(), getTraceAsString(), _toString(). Все методы являются финальными, кроме конструктора и _toString(). Таким образом, дополнительная функциональность классов-потомков Exception (отправка почты с информацией о ошибке, запись в log) может быть реализована в конструкторе.

Класс Exception объявляется непосредственно в PHP Engine, но его примерная модель может быть представленна таким образом (по материалам www.zend.com):

 class Exception {
          function 
__construct(string $message=NULLint $code=0) {
            if (
func_num_args()) {
              
$this->message $message;
            }
            
$this->code $code;
            
$this->file __FILE__// of throw clause
            
$this->line __LINE__// of throw clause
            
$this->trace debug_backtrace();
            
$this->string StringFormat($this);
          }

          protected 
$message 'Unknown exception';  // exception message
          
protected $code 0// user defined exception code
          
protected $file;     // source filename of exception
          
protected $line;     // source line of exception
  
          
private $trace;      // backtrace of exception
          
private $string;     // internal only!!

          
final function getMessage() {
            return 
$this->message;
          }
          final function 
getCode() {
            return 
$this->code;
          }
          final function 
getFile() {
            return 
$this->file;
          }
          final function 
getTrace() {
            return 
$this->trace;
          }
          final function 
getTraceAsString() {
            return 
self::TraceFormat($this);
          }
          function 
_toString() {
            return 
$this->string;
          }
          static private function 
StringFormat(Exception $exception) {
            
// ... a function not available in PHP scripts
            // that returns all relevant information as a string
          
}
          static private function 
TraceFormat(Exception $exception) {
            
// ... a function not available in PHP scripts
            // that returns the backtrace as a string
          
}
        } 

Использование объектов без ссылок на них

Очень серьезным неудобством в PHP4 было вызывание цепочки методов. В PHP4 невозможно создать объект без ссылки на него, поскольку объекты фактически являлись только синтаксичекой конструкцией и на уровне ядра были эквивалентны массивам. Это порождало, например, такие конструкции:

 $page = &$this->getPage();
$page->registerControl($this); 

Конечно, это не очень удобно. Созданная на уровне ядра PHP5, таблица ссылок на объекты делает необязательным существование ссылок на объект. Благодаря этому становится возможной следующая конструкция:

 $this->getPage()->registerControl($this); 

Но нужно заметить, что хотя такой подход и более краток по написанию, неразумное его использование чревато очень нерациональным кодом. Например, крайне не рекомендуется делать таким образом:

 for($i 0$i 100$i++)
    
$myObject->getProperty('relatedObject')->getAncestor($i)->update(); 

Во время работы этого кода осуществляется создание двухсот объектов и трехсот вызов методов. Очень простым образом можно сократить до создания ста объектов и двухсот одного вызова методов:

 $relatedObject $myObject->getProperty('relatedObject');
for(
$i 0$i 100$i++)
    
$relatedObject->getAncestor($i)->update(); 

Несмотря на очевидность такого подхода, довольно часто написанный код может быть улучшен с его помощью.

В следующих версиях PHP, скорее всего, можно ожидать расширения подобного подхода и к обычным массивам. Если они, конечно, еще останутся - объекты имеют тенденцию захватывать все больше и больше функциональности :-). Тогда, возможно, будет доступна следующая конструкция: print ((new ServerEnvironment()).getServerVariables())['REQUEST_URI'].

Инициализация переменных класса вне конструктора

Начальное значение переменной класса теперь можно указать непосредственно при её объявлении. Однако, её значение может быть только примитивного типа, т.е. строкой или числом. Тем не менее, этот метод является единственно возможным для задания значения статической переменной класса. Например:

 class MathUtils {
    static private 
pi 3.1415926;
    ...

По другому pi определить невозможно, поскольку для статических переменных не существует "статического" конструктора.

Статические методы класса

Статические методы класса могут быть вызванны непосредственно у класса, а не через его один из его объектов. Соответственно, указатель $this в статических методах недоступен.

Фактически, объявление класса со статическими методами является, в большей мере, методом группировки функций и общих для них констант и переменных. Например, столь известные в PHP функции соединения с MySQL можно было бы оформить в виде класса MySQL:

 interface DatabaseInterface {
    static function 
connect($host$user$password);
    static function 
select_db($database);
    static function 
query($query);
    static function 
fetch_array();
    static function 
free_result($result);
    static function 
close($link);
}

class 
MySQL implements DatabaseInterface {
    static 
CLIENT_COMPRESS 1;
    static 
CLIENT_IGNORE_SPACE 2;

    ...

    static function 
connect($host$user$password) {
        ...
    }

    static function 
select_db($database) {
        ...
    }

Применение такого подхода гарантирует, что все классы доступа к базе данных будут реализовывать один интерфейс (заменяемость), уменьшает вероятность конфликтности имен, упрощает существование нескольких версий класса доступа к базе и т.д.

instanceof оператор

Новый опреатор "проверяемый объект instanceof проверяемый класс" позволяет проверить, попадает ли проверяемый класс в список дерева наследования класса, экземпляром которого является проверяемый объект. На примере это выглядит так:

 interface Editable {
    function 
startEdit();
    function 
endEdit();
}

class 
Control {
    function 
getValue() {
        
//...
    
}
}

class 
EditableControl extends Control implements Editable {
    function 
startEdit() {
        
//...
    
}
    function 
endEdit() {
        
//...
    
}
}

$c = new Control();
$ec = new EditableControl();

print 
'$c instanceof Editable = ' . ($c instanceof Editable 'true' 'false') . '<br>';
print 
'$c instanceof Control = ' . ($c instanceof Control 'true' 'false') . '<br>';
print 
'$c instanceof EditableControl = ' . ($c instanceof EditableControl 'true' 'false') . '<br>';
print 
'$ec instanceof Editable = ' . ($ec instanceof Editable 'true' 'false') . '<br>';
print 
'$ec instanceof Control = ' . ($ec instanceof Control 'true' 'false') . '<br>';
print 
'$ec instanceof EditableControl = ' . ($ec instanceof EditableControl 'true' 'false'); 

Результатом работы этого кода будет:

 $c instanceof Editable false
$c 
instanceof Control true
$c 
instanceof EditableControl false
$ec 
instanceof Editable true
$ec 
instanceof Control true
$ec 
instanceof EditableControl true 

Таким образом, для $c instanceof возвращает true только для класса Control, для $ec instanceof вернет true только для Editable, Control, EditableControl. Для null всегда возращается false.

Статичекие переменные функций

Переменные внутри функции могут быть объявленны как static. Static переменная функции - это общая переменная для всех вызовов этой функции. Static переменная по смыслу примерно равна глобальной переменной, используемой только внутри функции.

Необязательные передающиеся по ссылке параметры функций

Передающиеся по ссылке параметры в PHP4 не могут иметь default значение. Это приводит к невозможности сделать функцию с необязательным объектным параметром. Но общественность требовала и в PHP5 появилась возможность задать для объектного параметра значение по умолчанию. Надо заметить, что возможно единственное значение по умолчанию для таких параметров - null.

Пример использования:

 class Unrequired {
    ...
}

function 
myFunction(Unrequired $param null) {
    ...
}

myFunction();
myFunction(new Unrequired()); 

Функция-событие при создании объекта неизвестного класса (__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>ASP</b>
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>PHP</b>
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. 

Итерация по свойствам класса

Все переменные класса, доступные в текущем контексте, могут быть перебранны циклом foreach. Такая итерация по свойствам класса может очень пригодится при клонировании объектов. Например, если необходимо создать клон объекта с большим количеством переменных класса, то можно сделать примерно так:

 class Node {
    private 
$value;
    private 
$parent;
    ...
    private 
$type;

    function 
__clone() {
        foreach (
$that as $propertyName => $propertyValue) {
            
$this->$propertyName $propertyValue;
        }
    }

    function 
setParent($value) { $this->parent $value; }
    function 
getParent() { return $this->parent; }
    function 
setValue($value) { $this->value $value; }
    function 
getValue() { return $this->value; }
    ...
    function 
setType($value) { $this->type $value; }
    function 
getType() { return $this->type; }
}

$myNode = new Node();
$myNode->setValue(10);
$myNextNode $myNode->__clone();
print 
$myNextNode->getValue(); 

Простой цикл очень хорошо заменяет большое количество присваиваний и избавляет от необходимости синхронизировать присваивания при клонировании со списком всех переменных класса. Очевидным образом, эта итерация не может быть применена к свойствам класса, реализованных через __get()/__set() функции.

Изменение стандартной итерации по свойствам

Стандартными элементами языка, позволящими итерацию, являются массивы. Но их недостатки очевидны - можно по ошибке поместить в массив элемент другого типа. К массиву не добавишь методы проверки содержимого и т.п.

Для введения дополнительных сущностей (классов), позволяющих итерацию по своим элементам, предусмотренно 2 интерфейса: IteratorAggregate и Iterator.

 interface IteratorAggregate {
    function 
getIterator(); // возвращает массив или объект

IteratorAggregate может использоваться, когда данные для итерации можно предоставить в одной из стандарных конструкций PHP, позволяющих итерацию: массива или объекта, реализующего Iterator.

Пример использования IteratorAggregate с итерацией по элементам массива:

 /**
    Замечание:
        Этот пример в PHP5 beta 3 не работает.
        Тем не менее, в документации заявленно, что getIterator()
        может возвращать массив или объект, реализующий Iterator.
        Так что к release, надеюсь, исправят.
*/
class Control implements IteratorAggregate {
    private 
$controls;
    private 
$name;

    function 
__construct() {
        
$this->controls = array();
    }

    function 
addControl($obj) {
        
$this->controls[$obj->getName()] = $obj;
    }

    function 
setName($value) { $this->name $value; }
    function 
getName() { return $this->name; }

    
// эта функция из IteratorAggregate
    
function getIterator() {
        return 
$this->controls;
    }
}

$c1 = new Control();
$c1->setName('userId');
$c2 = new Control();
$c2->setName('userName');
$form = new Control();
$form->addControl($c1);
$form->addControl($c2);

foreach (
$form as $ctrl) {
    echo 
$ctrl->getName() . '<br>';

Этот вариант является только надстройкой над стандартной функциональностью PHP и может использоваться когда до начала итерации можно получить все элементы, участвующие в итрации. Если же этого сделать нельзя (в случае получения записей из базы данных или чтении строк из большого файла), используется другой механизм расширения итерации - реализация интерфейса Iterator.

 interface Iterator {
    function 
rewind(); // переводит итерацию к первому элементу
    
function next(); // подготавливает к выводу следующий элемент
    
function key(); // возвращает ключ текущего элемента
    
function current(); // возвращает текущий элемент
    
function hasMore(); // возвращает true, если есть еще элементы, иначе false

Следующий пример показывает итерацию до нахождения нужного элемента:

 class Control {
    private 
$name;

    function 
setName($value) { $this->name $value; }
    function 
getName() { return $this->name; }
}

class 
Controls implements Iterator {
    private 
$controls;
    private 
$controlNames;
    private 
$num;

    function 
__construct() {
        
$this->controls = array();
    }

    function 
addControl($obj) {
        
$this->controls[$obj->getName()] = $obj;
    }

    
// функция использует возможности итерации класса для
    // поиска контрола с заданным именем
    
function findControl($name) {
        foreach (
$this as $control) {
            if (
$control->getName() == $name)
                return 
$control;
        }

        return 
null;
    }

    
// следующие функции из Iterator
    
function rewind() {
        
$this->controlNames array_keys($controls);
        
$this->num 0;
    }

    function 
next() {
        
$this->num++;
    }

    function 
key() {
        return 
$this->controlNames($this->num);
    }

    function 
current() {
        return 
$this->controls[$this->key()];
    }

    function 
hasMore() {
        return 
$this->num count($this->controlNames);
    }

}

$c1 = new Control();
$c1->setName('userId');
$c2 = new Control();
$c2->setName('userName');
$formControls = new Controls();
$formControls->addControl($c1);
$formControls->addControl($c2);

$userId $formControls->findControl('userId');
$userName $formControls->findControl('userName'); 

Константа __METHOD__

Константа __METHOD__ является хорошим дополнением к уже существующим "магическим" константам PHP4: __LINE__, __FILE__, __FUNCTION__ (с PHP4.3.0), __CLASS__ (с PHP4.3.0). Такие константы названны магическими, поскольку они меняют свое значение в зависимости от места вызова. Думаю, что вполне очевидно, что они возвращают, за исключением разницы между __FUNCTION__ и __METHOD__, поскольку функция класса и является его методом. Судя по всему, разработчики PHP5 решили, что константы __FUNCTION__, возвращающий только имя функции или метода класса, будет недостаточно и добавили константу __METHOD__, возврающую имя класса (в нижнем регистре) и имя метода, разделенные двумя двоеточиями.

Таким образом, следующий код выведет текст "myclass|myMethod|myclass::myMethod":

 Class MyClass {
    function 
myMethod() {
        echo 
__CLASS__ '|' __FUNCTION__ '|' __METHOD__;
    }
}

$m = new MyClass();
$m->myMethod(); 

Метод __toString()

Когда переменная-объект преобразуется к строке, в результате возвращается строка "Object id #n", где n - номер объекта в глобальной таблице объектов. Если понадобится (пусть и крайне редко), этот механизм можно изменить, создав у класса метод __toString(), возвращающий некоторое строковое представление текущего объекта.

Хотя PHP5 beta 3 этот алгоримт проработан не полностью (__toString() срабатывает только во время использования указателя на объект в операторе print), это открывает интересные перспективы. Например, следующий код представляет собой вариацию на тему типизации PHP:

 class Integer {
    private 
$value;
    function 
__construct($val) {
        
$this->value $val;
    }
    function 
__toString() {
        return (string)(
$this->value);
    }
}

$i = new Integer(10);
/**
    Теоритически, $i при преобразовании к строке должно дать "10",
    и, поскольку число 10 сравнивается со строкой, оно тоже должно
    быть приведенно к строке. Получится "10" == "10". На практике, в
    этом случае преобразование $i к строке осуществляется по варианту
    PHP4 (т.е. в результате получаем строку "Object").
*/

if (10 == $i)
    echo 
'10!!!! :-)'

Reflection API

Reflection не является новым понятием для PHP, но только в PHP5 предпринята попытка привести работу со структурными объектами языка к общему виду. Под структурными объектами понимаются функции, классы, интерфейсы, параметры и расширения.

Классы Reflection позволяют получать информацию о объектах языка непосредственно во время выполнения скрипта. Например, можно получить информацию о некотором объекте, включая его методы, их параметры, в каком файле находится описание объекта и даже какой документационный комментарий находится перед ним.

Reflection классы сделанны для каждого структурного объекта языка:

  • Reflection_Function
  • Reflection_Parameter
  • Reflection_Method
  • Reflection_Class
  • Reflection_Property
  • Reflection_Extension

В отличии от большинства других изменений в PHP5, Reflections уже неплохо документированны. Подробное описание доступно по адресу http://sitten-polizei.de/php/reflection_api/docs/language.reflection.html.

Пример использования Reflection:

 /**
    MyClass просто пример класса для демонстрации Reflection.
    Этот код будет выведен как документация к классу MyClass при Reflection.
*/
class MyClass {

    
/**
        А это комментарий к конструктору
    */
    
function __construct() {
    }
}

Reflection::export(new Reflection_Class('MyClass')); 

Результатом работы этого кода будет следующее описание класса:

 /**
    MyClass просто пример класса для демонстрации Reflection.
    Этот код будет выведен как документация к классу MyClass при Reflection.
*/
Class [ <user>  class myclass ] {
  @@ /
home/alex/public_html/devlink_draft/articles/docs/test.php 7-14

  
Constants [0] {
  }

  - Static 
properties [0] {
  }

  - Static 
methods [0] {
  }

  - 
Properties [0] {
  }

  - 
Methods [1] {
    
/**
        А это комментарий к конструктору
    */
    
Method [ <user> <ctor> public method __construct ] {
      @@ /
home/alex/public_html/devlink_draft/articles/docs/test.php 12 13
    
}
  }


For comment register here
   2004-04-10 10:26

Обзор новых возможностей PHP5: изменения в объектной модели, новые функции и синтаксические конструкции Переход к PHP5 Zend Engine 2 ZE новые возможности PHP перспектива

 
 
 
    © 1997-2008 PHPClubTeam
[]