Используя новые возможности языка PHP V5, можно значительно повысить удобство сопровождения и надежность кода. Прочитав эту статью, вы узнаете, как воспользоваться этими новыми функциями для переноса кода, разработанного в PHP V4, на PHP V5.
PHP V5 содержит значительные улучшения по сравнению с PHP V4. Новые возможности языка упрощают создание надежных библиотек классов и их обслуживание. Кроме того, переписывание стандартной библиотеки помогло привести PHP в большее соответствие с другими веб-идиомами, такими как язык программирования Java™. Давайте рассмотрим некоторые новые объектно-ориентированные функции PHP и узнаем, как перенести существующий код PHP V4 в PHP V5.
Во-первых, давайте посмотрим, как новые возможности языка и создатель PHP изменили способ создания объектов в PHP V4. Идея V5 заключалась в создании промышленного языка для разработки веб-приложений. Это означает понимание ограничений PHP V4, а затем извлечение известных хороших языковых архитектур из других языков (таких как Java, C#, C++, Ruby и Perl) и включение их в PHP.
Первой и наиболее важной новой функцией является защита доступа к методам класса и переменным экземпляра — ключевым словам public, protected и Private. Эта новая функция позволяет разработчикам классов сохранять контроль над внутренними свойствами класса, сообщая пользователям класса, какие классы доступны, а какие нет.
В PHP V4 весь код является общедоступным. В PHP V5 дизайнеры классов могут объявлять, какой код виден внешнему миру (общедоступный), а какой код виден только внутри класса (частный) или только подклассам класса (защищенный). Без этих средств управления доступом разработка кода в большой команде или распространение кода в виде библиотеки затруднены, поскольку пользователи этих классов, скорее всего, будут использовать неправильные методы или код доступа, который должен быть закрытыми переменными-членами.
Еще одна важная новая функция — интерфейс ключевых слов и аннотация, которые позволяют программировать по контрактам. Контрактное программирование означает, что один класс предоставляет контракт другому классу — другими словами: «Это то, что я собираюсь сделать, и вам не нужно знать, как это делается». Все классы, реализующие интерфейс, придерживаются этого контракта. Все пользователи интерфейса соглашаются использовать только те методы, которые указаны в интерфейсе. Ключевое слово абстрактное упрощает работу с интерфейсами, как я объясню позже.
Эти две ключевые функции — контроль доступа и контрактное программирование — позволяют большим группам программистов более эффективно работать с большими базами кода. Эти функции также позволяют IDE предоставлять более богатый набор интеллектуальных функций языка. В этой статье не только рассматриваются некоторые проблемы миграции, но также уделяется некоторое время объяснению того, как использовать эти новые основные возможности языка.
Контроль доступа.
Чтобы продемонстрировать новые возможности языка, я использовал класс Configuration. Этот простой класс содержит элементы конфигурации веб-приложения, например путь к каталогу изображений. В идеале эта информация должна храниться в файле или базе данных. В листинге 1 показана упрощенная версия.
Листинг 1. access.php4
<?php
Конфигурация класса
{
вар $_items = массив()
функция Configuration() {;
$this->_items['imgpath'] = 'изображения';
}
функция get($key) {
вернуть $this->_items[$key];
}
}
$c = новая конфигурация();
echo($c->get('imgpath')."n" );
?>
Это совершенно ортодоксальный класс PHP V4. Переменная-член содержит список элементов конфигурации, конструктор загружает элементы, а метод доступа с именем get() возвращает значение элемента.
После запуска скрипта в командной строке появится следующий код:
%php access.php4
изображения
%
очень хороший! Этот результат означает, что код работает нормально, а значение элемента конфигурации imgpath устанавливается и читается нормально.
Первым шагом преобразования этого класса в PHP V5 является переименование конструктора. В PHP V5 метод инициализации объекта (конструктора) называется __construct. Это небольшое изменение показано ниже.
Листинг 2. access1.php5
<?php
Конфигурация класса
{
вар $_items = массив()
функция __construct() {
$this->_items['imgpath'] = 'изображения';
}
функция get($key) {
вернуть $this->_items[$key];
}
}
$c = новая конфигурация();
echo($c->get('imgpath')."n" );
?>
Изменения на этот раз не большие. Только что перешел на соглашение PHP V5. Следующим шагом является добавление контроля доступа к классу, чтобы гарантировать, что пользователи класса не смогут напрямую читать и записывать переменную-член $_items. Это изменение показано ниже.
Листинг 3. access2.php5
<?php
Конфигурация класса
{
частный $_items = массив ()
общественная функция __construct () {
$this->_items['imgpath'] = 'изображения';
}
публичная функция get($key) {
вернуть $this->_items[$key];
}
}
$c = новая конфигурация();
echo($c->get('imgpath')."n" );
?>
Если бы пользователь этого объекта имел прямой доступ к массиву элементов, доступ был бы запрещен, поскольку массив помечен как частный. К счастью, пользователи обнаружили, что метод get() предоставляет долгожданные разрешения на чтение.
Чтобы проиллюстрировать, как использовать защищенные разрешения, мне нужен еще один класс, который должен наследовать от класса Configuration. Я назвал этот класс DBConfiguration и предположил, что класс будет читать значения конфигурации из базы данных. Эта установка показана ниже.
Листинг 4. access3.php
<?php
Конфигурация класса
{
защищенный $_items = массив ();
общественная функция __construct () {
$this->load();
}
защищенная функция load() { }
публичная функция get($key) {
вернуть $this->_items[$key];
}
}
Класс DBConfiguration расширяет конфигурацию
{
защищенная функция load() {
$this->_items['imgpath'] = 'изображения';
}
}
$c = новая DBConfiguration();
echo($c->get('imgpath')."n" );
?>
Этот листинг показывает правильное использование ключевого слова protected. Базовый класс определяет метод load(). Подклассы этого класса переопределяют метод load() для добавления данных в таблицу элементов. Метод load() является внутренним для класса и его подклассов, поэтому он не виден всем внешним потребителям. Если все ключевые слова являются частными, метод load() не может быть переопределен.
Мне не очень нравится этот дизайн, но я выбрал его, потому что мне нужно было предоставить классу DBConfiguration доступ к массиву элементов. Я бы хотел, чтобы массив элементов полностью обслуживался классом Configuration, чтобы при добавлении других подклассов этим классам не нужно было знать, как поддерживать массив элементов. Я внес следующие изменения.
Листинг 5. access4.php5
<?php
Конфигурация класса
{
частный $_items = массив ()
общественная функция __construct () {
$this->load();
}
защищенная функция load() { }
защищенная функция add($key, $value) {
$this->_items[$key] = $value;
}
публичная функция get($key) {
вернуть $this->_items[$key];
}
}
Класс DBConfiguration расширяет конфигурацию
{
защищенная функция load() {
$this->add('imgpath', 'images');
}
}
$c = новая DBConfiguration();
echo($c->get('imgpath')."n" );
?>
Массивы элементов теперь могут быть частными, поскольку подклассы используют защищенный метод add() для добавления элементов конфигурации в список. Класс Configuration может изменять способ хранения и чтения элементов конфигурации независимо от его подклассов. Пока методы load() и add() выполняются одинаково, с созданием подклассов не должно возникнуть проблем.
Для меня добавленный контроль доступа является основной причиной рассмотреть возможность перехода на PHP V5. Только ли потому, что Грэди Буч сказал, что PHP V5 — один из четырех основных объектно-ориентированных языков? Нет, потому что однажды я принял задание поддерживать код 100KLOC C++, в котором все методы и члены были определены как общедоступные. На очистку этих определений у меня ушло три дня, и в процессе я значительно сократил количество ошибок и улучшил ремонтопригодность. Почему? Потому что без контроля доступа невозможно узнать, как объекты используют другие объекты, и невозможно внести какие-либо изменения, не зная, какие препятствия нужно преодолеть. Что касается C++, по крайней мере, у меня все еще есть компилятор. PHP не поставляется с компилятором, поэтому этот тип контроля доступа становится еще более важным.
Контрактное программирование
Следующей важной особенностью, которой следует воспользоваться при переходе с PHP V4 на PHP V5, является поддержка контрактного программирования через интерфейсы, абстрактные классы и методы. В листинге 6 показана версия класса Configuration, в которой программисты PHP V4 попытались создать базовый интерфейс вообще без использования ключевого слова интерфейса.
Листинг 6. Interface.php4
<?php
класс IКонфигурация
{
функция get($key) { }
}
Конфигурация класса расширяет IConfiguration
{
вар $_items = массив()
функция Configuration() {;
$this->load();
}
функция загрузки() { }
функция get($key) {
вернуть $this->_items[$key];
}
}
Класс DBConfiguration расширяет конфигурацию
{
функция загрузки() {
$this->_items['imgpath'] = 'изображения';
}
}
$c = новая DBConfiguration();
echo($c->get('imgpath')."n" );
?>
Листинг начинается с небольшого класса IConfiguration, который определяет все интерфейсы, предоставляемые классом Configuration или производными классами. Этот интерфейс будет определять контракт между классом и всеми его пользователями. В контракте указано, что все классы, реализующие IConfiguration, должны быть оснащены методом get() и что все пользователи IConfiguration должны настаивать на использовании только метода get().
Приведенный ниже код выполняется в PHP V5, но лучше использовать предоставленную систему интерфейса, как показано ниже.
Листинг 7. Interface1.php5
<?php
интерфейс IConfiguration
{
функция get($key);
}
Конфигурация класса реализует IConfiguration
{
...
}
Класс DBConfiguration расширяет конфигурацию
{
...
}
$c = новая DBConfiguration();
echo($c->get('imgpath')."n" );
?>
С одной стороны, читатели могут более четко понимать статус работы, с другой стороны, один класс может реализовывать несколько интерфейсов; В листинге 8 показано, как расширить класс Configuration для реализации интерфейса Iterator, который является внутренним интерфейсом PHP.
Листинг 8. Interface2.php5
<?php
интерфейс IConfiguration {
...
}
Конфигурация класса реализует IConfiguration, Iterator
{
частный $_items = массив ()
общественная функция __construct () {
$this->load();
}
защищенная функция load() { }
защищенная функция add($key, $value) {
$this->_items[$key] = $value;
}
публичная функция get($key) {
вернуть $this->_items[$key];
}
Общественная функция rewind() {reset($this->_items });
общественная функция current() { return current($this->_items });
публичный функциональный ключ () {ключ возврата ($ this-> _items });
общественная функция next() { return next($this->_items });
общественная функция valid() { return ( $this->current() !== false });
}
Класс DBConfiguration расширяет конфигурацию {
...
}
$c = новая DBConfiguration();
foreach($c as $k => $v) { echo( $k." = ".$v."n" };
?>
Интерфейс Iterator позволяет любому классу выглядеть как массив его потребителей. Как вы можете видеть в конце сценария, вы можете использовать оператор foreach для повторения всех элементов конфигурации в объекте Configuration. PHP V4 не имеет этой функции, но вы можете использовать ее в своем приложении различными способами.
Преимущество механизма интерфейса в том, что контракты можно быстро объединять без необходимости реализации каких-либо методов. Завершающий этап — реализация интерфейса, где необходимо реализовать все указанные методы. Еще одна полезная новая функция PHP V5 — абстрактные классы, которые позволяют легко реализовать основную часть интерфейса с помощью базового класса, а затем использовать этот интерфейс для создания классов сущностей.
Другое использование абстрактных классов — создание базового класса для нескольких производных классов, в которых базовый класс никогда не создается. Например, если DBConfiguration и Configuration существуют одновременно, можно использовать только DBConfiguration. Класс Configuration — это просто базовый класс, абстрактный класс. Таким образом, вы можете принудительно настроить такое поведение, используя ключевое слово абстрактное, как показано ниже.
Листинг 9. Abstract.php5
<?php
Конфигурация абстрактного класса
{
защищенный $_items = массив ();
общественная функция __construct () {
$this->load();
}
абстрактная защищенная функция load();
публичная функция get($key) {
вернуть $this->_items[$key];
}
}
Класс DBConfiguration расширяет конфигурацию
{
защищенная функция load() {
$this->_items['imgpath'] = 'изображения';
}
}
$c = новая DBConfiguration();
echo($c->get('imgpath')."n" );
?>
Теперь все попытки создать экземпляр объекта типа Configuration будут завершаться ошибкой, поскольку система считает класс абстрактным и неполным.
Статические методы и члены
Еще одна важная новая функция PHP V5 — поддержка статических членов и методов классов. Используя эту функциональность, вы можете использовать популярный шаблон Singleton. Этот шаблон идеально подходит для класса Configuration, поскольку приложение должно иметь только один объект конфигурации.
В листинге 10 показана версия класса Configuration PHP V5 как одноэлементная.
Листинг 10. static.php5
<?php
Конфигурация класса
{
частный $_items = массив ()
статический частный $_instance = null;
статическая публичная функция get() {
если (self::$_instance == null)
self::$_instance = новая конфигурация();
вернуть себя::$_instance;
}
частная функция __construct() {
$this->_items['imgpath'] = 'изображения';
}
публичная функция __get($key) {
вернуть $this->_items[$key];
}
}
echo( Configuration::get()->{ 'imgpath' }."n" );
?>
Ключевое слово static имеет множество применений. Рассмотрите возможность использования этого ключевого слова, когда вам нужно получить доступ к некоторым глобальным данным для всех объектов одного типа.
Магический метод
Еще одной важной новой функцией PHP V5 является поддержка магических методов, которые позволяют объектам быстро изменять интерфейс объекта, например, добавляя переменные-члены для каждого элемента конфигурации в объекте Configuration. Нет необходимости использовать метод get(), просто найдите конкретный элемент и обработайте его как массив, как показано ниже.
Листинг 11. Magic.php5
<?php
Конфигурация класса
{
частный $_items = массив()
функция __construct() {;
$this->_items['imgpath'] = 'изображения';
}
функция __get($key) {
вернуть $this->_items[$key];
}
}
$c = новая конфигурация();
echo( $c->{ 'imgpath' }."n" );
?>
В этом примере я создал новый метод __get(), который вызывается всякий раз, когда пользователь ищет переменную-член объекта. Затем код внутри метода будет использовать массив элементов, чтобы найти значение и вернуть это значение, как если бы там была переменная-член, специально предназначенная для этого ключевого слова. Предполагая, что объект представляет собой массив, в конце скрипта вы увидите, что использовать объект Configuration так же просто, как найти значение imgpath.
При переходе с PHP V4 на PHP V5 вы должны знать об этих функциях языка, которые полностью недоступны в PHP V4, и вам необходимо повторно проверить классы, чтобы увидеть, как их можно использовать.
Исключения
завершают эту статью описанием нового механизма исключений в PHP V5. Исключения предоставляют совершенно новый подход к обработке ошибок. Все программы неизбежно выдают ошибки - файл не найден, недостаточно памяти и т.п. Если исключения не используются, должен быть возвращен код ошибки. Пожалуйста, посмотрите код PHP V4 ниже.
Листинг 12. file.php4
<?php
функция parseLine($l)
{
// ...
возвращаемый массив( 'ошибка' => 0,
data => array() // здесь данные
);
}
Функция readConfig($path)
{
если ($path == null) вернуть -1;
$fh = fopen($path, 'r');
if ($fh == null) return -2
while(!feof($fh)) {;
$l = fgets($fh);
$ec = parseLine($l);
if ( $ec['error'] != 0 ) return $ec['error'];
}
fclose($fh);
вернуть 0;
}
$e = readConfig('myconfig.txt');
если ($е!= 0)
echo( "Произошла ошибка (".$e.")n" );
?>
Этот стандартный код файлового ввода-вывода читает файл, извлекает некоторые данные и возвращает код ошибки, если возникают какие-либо ошибки. У меня есть два вопроса по поводу этого сценария. Первый — это код ошибки. Что означают эти коды ошибок? Чтобы выяснить, что означают эти коды ошибок, необходимо создать другую систему для преобразования этих кодов ошибок в значимые строки. Вторая проблема заключается в том, что возвращаемый результат parseLine очень сложен. Мне просто нужно, чтобы он возвращал данные, но на самом деле он должен возвращать код ошибки и данные. Большинство инженеров (включая меня) часто ленятся и просто возвращают данные и игнорируют ошибки, потому что с ошибками трудно справиться.
Листинг 13 показывает, насколько понятен код при использовании исключений.
Листинг 13. file.php5
<?php
функция parseLine($l)
{
// Анализирует и выдает исключение, если оно недействительно
вернуть массив(); // данные
}
Функция readConfig($path)
{
если ($path == null)
throw new Exception('плохой аргумент');
$fh = fopen($path, 'r');
если ($fh == ноль)
throw new Exception('не удалось открыть файл' );
while( !feof($fh) ) {
$l = fgets($fh);
$ec = parseLine($l);
}
fclose($fh);
}
пытаться {
readConfig('myconfig.txt');
} catch(Исключение $e) {
эхо ($ е);
}
?>
Мне не нужно беспокоиться о кодах ошибок, поскольку исключение содержит текст описания ошибки. Мне также не нужно думать о том, как отследить код ошибки, возвращаемый функцией parseLine, потому что функция просто выдаст ошибку, если она произойдет. Стек распространяется до ближайшего блока try/catch, который находится в нижней части скрипта.
Исключения произведут революцию в написании кода. Вместо головной боли, связанной с кодами ошибок и сопоставлениями, вы можете сосредоточиться на ошибках, которые хотите обработать. Такой код легче читать, поддерживать и, я бы сказал, даже поощрять вас добавлять обработку ошибок, поскольку это обычно приносит дивиденды.
Заключение
Новые объектно-ориентированные функции и добавление обработки исключений дают веские основания для перехода с PHP V4 на PHP V5. Как видите, процесс обновления не сложный. Синтаксис, распространяемый на PHP V5, аналогичен PHP. Да, эти синтаксисы взяты из таких языков, как Ruby, но я думаю, что они очень хорошо работают вместе. И эти языки расширяют сферу применения PHP от языка сценариев для небольших сайтов до языка, который можно использовать для завершения приложений уровня предприятия.