Что нового в PHP V5.3: Часть 1. Изменения в интерфейсе объектов

В ближайшее время должна быть выпущена версия PHP V5.3. Многие новые функции этой версии готовились несколько лет. Версия PHP V5.3, которую вначале называли «PHP V6 без встроенной поддержки Unicode», превратилась в функционально насыщенное обновление линейки PHP V5. Добавив множество новых функций, оптимизировав существующие функции, исправив проблемы на отдельных платформах и вычистив старые функции, которых не будет в следующих версиях РНР, она должна подготовить разработчиков к выходу версии PHP V6. В серии статей "Что нового в PHP V5.3" мы рассмотрим эти новые функции V5.3 и увидим, как они применяются и как их можно использовать в вашем Web-приложении.

Джон Мертик, инженер-программист, SugarCRM

Джон Мертик (John Mertic) получил диплом инженера по вычислительной технике в университете Kent State University и в настоящее время работает инженером-программистом в компании SugarCRM. Он участвовал во многих проектах open source, главным образом в РНР-проектах; является создателем и хранителем PHP Windows Installer.



04.12.2009

PHP V5 и объектно-ориентированное программирование

В 2004 году, когда вышел PHP V5, был сделан гигантский шаг вперед по сравнению с версией РНР V4 с точки зрения объектно-ориентированного программирования (ООП) и проектирования. Был добавлен ряд необходимых усовершенствований, таких как видимость классов, полные конструкторы и деструкторы, подсказки по типам и API отражения классов (class-reflection API). Это открыло путь к объектно-ориентированному программированию на РНР и позволило гораздо легче реализовывать многие модели программирования и лучше проектировать классы и API.

В PHP V5.3 появились многочисленные дополнения, облегчающие ООП, как в части синтаксиса, так и в части производительности. Для начала рассмотрим новые функции, относящиеся к статическим методам и членам классов.


Усовершенствование статических методов и управления членами классов

Одно из полезных новшеств PHP V5 заключалось в возможности определять метод или член класса как статический (версия PHP V4 поддерживала статический доступ к методам и членам класса, но не позволяла определять их как метод или член класса, предназначенный для статического доступа). Статический доступ особенно полезен для реализации синглтонного шаблона проектирования, когда существует только один экземпляр класса.

В PHP V5.3 добавилось несколько функций для улучшения поддержки статических членов и методов внутри класса. Сначала рассмотрим новый магический метод: __callStatic().

Магический метод _callStatic()

В PHP V5 есть несколько специально определенных так называемых магических методов, которые можно использовать внутри классов. Эти методы, когда они определены в классе, обеспечивают специальную функциональность и возможности перегрузки (когда метод может принимать параметры разного типа) и полиморфизма (когда разные типы данных используют один и тот же интерфейс). Кроме того, они позволяют легко применять в РНР различные методы ООП и шаблоны проектирования.

В PHP V5.3 добавлен новый магический метод: __callStatic(). Он работает аналогично магическому методу __call(), предназначенному для управления вызовами методов, которые не определены или не видимы в классе. Однако __callStatic() применяется для управления вызовами статических методов, что позволяет нам лучше проектировать перегрузку своего метода. Ниже приводится пример использования этого метода.

Листинг 1. Пример: сравнение методов __callStatic() и __call()
class Foo 
{ 
    public static function __callStatic( 
        $name, 
        $args 
        ) 
    { 
        echo "Called method $name statically"; 
    } 

    public function __call( 
        $name, 
        $args 
        ) 
    { 
        echo "Called method $name"; 
    } 
} 

Foo::dog();       // выводит "Called method dog statically" 
$foo = new Foo; 
$foo->dog();      // выводит "Called method dog"

Надо отметить, что в РНР определение метода __callStatic() строго регламентируется; он должен быть публичным и определенным как статический. Аналогично, магический метод __call(), как и все магические методы, тоже должен быть определен как публичный.


Динамический доступ к статическим данным

Одна интересная особенность РНР — переменные с изменяемыми именами (variable variables). Это означает, что для задания имени переменной можно использовать строковое значение другой переменной. Иными словами, можно делать нечто подобное тому, что показано в листинге 2.

Листинг 2. Динамический доступ к статическим данным
    $x = 'y';
    $$x = 'z';
    echo $x;  // outputs 'y'
    echo $y;  // outputs 'z'
    echo $$x; // outputs 'z'

Ту же идею можно применять по отношению к функциям или даже методам класса (листинг 3).

Листинг 3. Переменные имена функции и метода класса
class Dog 
{ 
    public function bark() 
    { 
        echo "Woof!"; 
    } 
} 

$class = 'Dog' 
$action = 'bark'; 
$x = new $class(); // создает класс 'Dog' 
$x->$action();     // выводит "Woof!"

В PHP V5.3 добавлена возможность указывать при статических вызовах переменное имя класса. Это открывает ряд новых возможностей (листинг 4).

Листинг 4. Переменные имена классов
class Dog 
{ 
    public static function bark() 
    { 
         echo "Woof!"; 
    } 
} 

$class = 'Dog'; 
$action = 'bark'; 
$class::$action();  //выводит "Woof!"

Это нововведение делает функцию PHP variable-variables завершенной, позволяя использовать эти переменные почти в любой ситуации.

Рассмотрим еще одно полезное усовершенствование в использовании статических методов и членов: позднее статическое связывание (late static binding).


Позднее статическое связывание

Одним недостатком РНР до версии 5.3 была организация работы статических методов с членами классов. Статические ссылки, такие как self или __CLASS__, обрабатываются в том классе, в котором была определена функция. Если же класс был расширен и вызов произведен из нового дочернего класса, ссылка окажется неправильной. Для решения этой проблемы в PHP V5.3 добавлено позднее статическое связывание. Чтобы лучше проиллюстрировать эту функцию, создадим класс со статическим методом.

Листинг 5. Класс Foo со статическим методом test()
class Foo 
{ 
    protected static $name = 'Foo'; 

    public static function test() 
    { 
        return self::$name; 
    } 
}

Теперь расширим этот класс. Переопределим член $name в этом дочернем классе.

Листинг 6. Дочерний класс Bar, расширяющий родительский класс Foo
    class Bar extends Foo
    {
       protected static $name = 'Bar';
    }

Выполним статический вызов (листинг 7).

Листинг 7. Статический вызов метода test()
echo Bar::test();

Результатом этого вызова будет строка Foo. Дело в том, что ссылка self::$name в методе test() выполняется по отношению к классу Foo. Это происходит потому, что функция была определена именно в нем.

В PHP V5.3 добавилось ключевое слово static, что позволяет делать ссылки по отношению к текущему классу. Изменим определенный выше класс Foo с применением этого ключевого слова (листинг 8), и вместо него будет выводиться класс Bar.

Листинг 8. Применение ключевого слова static
class Foo 
{ 
    protected static $name = 'Foo'; 

    public static function test() 
    { 
        return static::$name; 
    } 
} 

class Bar 
{ 
    protected static $name = 'Bar'; 
} 

echo Bar::test(); // выводит 'Bar'

Важно помнить, что здесь ключевое слово static работает не так, как в нестатическом контексте. Обычные правила наследования неприменимы к статическим вызовам. Ключевое слово static просто пытается отнести вызов к текущему классу, а не к классу, в котором была определена функция.

Теперь рассмотрим некоторые новые классы, добавленные в очень полезную часть PHP V5 — стандартную библиотеку PHP.


Стандартная библиотека PHP

Стандартная библиотека PHP (Standard PHP Library - SPL) — это набор интерфейсов и классов, введенных в PHP V5 для решения стандартных задач. В число этих задач входит превращение объекта в итерируемый (iterateable), обращение с объектом как с массивом и реализация связанного списка. Преимущество использования этих классов и методов состоит в том, что они встроены в РНР, а, значит, работают быстрее, чем при их реализации средствами самого РНР. К тому же во многих случаях для выполнения итераций с объектом они позволяют использовать конструкцию foreach.

В PHP V5.3 в SPL добавлено несколько новых классов. Один из них, о котором мы уже упоминали, это реализация дважды связанного списка в классе SPL SplDoublyLinkedList. Его используют два других новых класса SPL: SplStack, который реализует стек, и SplQueue, который реализует очередь.

Рассмотрим, как класс SplStack можно использовать для реализации стека.

Листинг 9. Использование класса SplStack
$stack = new SplStack(); 

// поместим в стек несколько новых позиций 
$stack->push('a'); 
$stack->push('b'); 
$stack->push('c'); 

// вот сколько позиций находится в стеке 
echo count($stack); // выдает 3 

// итерации с позициями стека 
foreach ( $stack as $item ) 
    echo "[$item],";   
// выходные данные: [c],[b],[a]

// выталкивание позиции из стека 
echo $stack->pop(); // выдает 'c' 

// посмотрим, сколько позиций осталось в стеке 
echo count($stack); // выдает 2

Класс SqlQueue работает аналогично, но реализует очередь («первым вошел — первым вышел»; а не «последним вошел — первым вышел», как в стеке). Кроме того, существует реализация «кучи» (SplHeap), а также специальные реализации очереди и кучи для определенных ситуаций (SplMinHeap, SplMaxHeap и SplPriorityQueue).

Другим полезным дополнением служит класс SplFixedArray, который, как следует из названия, представляет собой реализацию массива фиксированного размера. Однако он весьма быстрый — настолько, что в тестах работает на 10-30% быстрее, чем реализация массива, встроенная в РНР. Причина в том, что у этого массива фиксированный, а не переменный размер, как у массива РНР по умолчанию, и в нем не допускаются нецифровые индексы. Пример его применения приведен в листинге 10.

Листинг 10. SplFixedArray
$array = new SplFixedArray(3); 
$array[0] = 'dog'; 
$array[1] = 'cat'; 
$array[2] = 'bird'; 
$a->setSize(4); // динамически увеличивает размер 
$array[3] = 'mouse'; 
foreach ( $array as $value ) 
    echo "[$value],";

Выход: 
[dog],[cat],[bird],[mouse]

Еще добавлено несколько новых классов iterator: FilesystemIterator и GlobIterator. Они работают так же, как и другие классы iterator в РНР, но предназначены для особых случаев.

Другое изменение в SPL заключается в том, что в PHP V5.3 она всегда включена. В предыдущих версиях PHP V5 можно было отключить SPL на этапе компиляции, но в PHP V5.3 это невозможно.

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

Теперь, когда мы рассмотрели новые дополнения к SPL, посмотрим, как в PHP V5.3 улучшены производительность и использование памяти при OOП благодаря введению круговой сборки мусора.


Круговая сборка мусора

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

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

Листинг 11. Отношение классов родительский-дочерний приводит к неправильной сборке мусора в PHP V5.2 и более ранних версиях
class Parent 
{ 
    public function __construct() 
    { 
        $this->child = new Child($this); 
    } 
} 

class Child 
{ 
    public function __construct( 
        Parent $parent 
        ) 
    { 
        $this->parent = $parent; 
    } 
}

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

В PHP V5.3 сборщик мусора обнаруживает такие круговые ссылки и может освобождать занимаемую ими память, так что объем памяти, используемой сценарием РНР, остается постоянным. При удалении каждой ссылки на класс Parent сборщик мусора удаляет ссылку на класс Child внутри класса Parent.


Заключение

PHP прошел большой путь в части поддержки объектно-ориентированного программирования — от слабой поддержки в PHP V4 до значительно усовершенствованной в PHP V5 с дальнейшими дополнениями в последующих версиях. В новой версии PHP V5.3 появились новые замечательные дополнения, включая такие усовершенствования в области синтаксиса, как новые магические методы __callStatic(), динамический доступ к статическим данным, позднее статическое связывание, статический метод и поддержка членов класса. Появились новые дополнения к SPL с реализациями дважды связанных списков, стеков, куч и очередей, что упростило создание и использование некоторых полезных структур данных. Наконец, долгожданный циклический сборщик мусора решил проблемы утечки памяти и производительности, связанные с самоссылающимися классами, значительно улучшив процесс сборки мусора в ситуациях кругового вызова. Все эти новшества делают PHP V5.3 гораздо более мощным языком с точки зрения объектно-ориентированного программирования.

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=453058
ArticleTitle=Что нового в PHP V5.3: Часть 1. Изменения в интерфейсе объектов
publish-date=12042009