Содержание


Обновленный PHP

Новое лицо PHP

Познакомьтесь с новыми мощными средствами языка PHP

Comments

Серия контента:

Этот контент является частью # из серии # статей: Обновленный PHP

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Обновленный PHP

Следите за выходом новых статей этой серии.

Язык PHP поддерживается и разрабатывается как проект с открытым исходным кодом, имеющий сотни — а возможно и тысячи — участников, активно работающих над развитием языка, чтобы удовлетворить потребности современной веб-разработки. PHP продолжает включать в себя новые идеи программирования и заимствует идеи из других языков программирования, сохраняя при этом высокий уровень обратной совместимости. Эти качества обеспечили языку PHP его нынешнее выдающееся положение: он обеспечивает функционирование 82% мирового Web, включая некоторые из крупнейших веб-сайтов (таких как Facebook). Кроме того, PHP является базовой технологией в инфраструктурах систем управления контентом, таких как WordPress, Drupal, Magento и Joomla! (которые совместно обеспечивают функционирование примерно 30% мирового Web).

Если вы длительное время (или даже несколько последних лет) не имели дела с PHP, вы можете не узнать язык, в который он превратился. Эта статья, первая в цикле из четырех частей, представляет новейшие возможности, появившиеся в недавних версиях языка PHP (5.3, 5.4 и 5.5).

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

Я не думаю, что кто-то что-то крадет; все мы берем взаймы

Би Би Кинг

Пространства имен

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

До того как была реализована встроенная поддержка пространств имен, библиотеки преодолевали эту проблему, снабжая имена всех своих классов постоянным префиксом (например, префиксом Zend_ в случае инфраструктуры Zend Framework). Если разработчик пользуется эзотерическими именами классов, такими как Zend_Db_Table, ему придется многократно вводить такие префиксы при написании кода. Эта проблема достигла своей кульминации, когда в ядро PHP версии 5.2 были добавлены классы DateTime (весьма необходимые). Неожиданно многие библиотеки с открытым исходным кодом начали испытывать сбои, поскольку в них уже были созданы свои собственные классы с именем DateTime, чтобы восполнить имевшийся на тот момент пробел.

Пространства имен создаются с помощью ключевого слова namespace и отделяются символом "\". Простой пример показан в листинге 1.

Листинг 1. Простой пример использования пространства имен
 <?php namespace zebra; class DateTime { public function __construct() { echo "Today!"; } } function stripes() { echo "-=-=-=-=-=-=-=-"; }

В Листинг 1 я задаю свое собственное пространство имен с именем zebra, а затем определяю внутри него класс и функцию. В этом случае повторное определение класса DateTime не порождает проблем или ошибок, поскольку я создаю свою версию DateTime в пространстве имен. Теперь я могу использовать это пространство имен, используя для ссылки полное имя с символом "\" в качестве разделителя (см. листинг 2).

Листинг 2. Использование специального пространства имен
 <?php include 'listing1.php'; // Использование функции stripes, которую я объявил в своем пространстве имен: zebra\stripes(); // Использование моего класса DateTime: $dt = new zebra\DateTime(); // Использование класса DateTime на уровне 'root' $real = new \DateTime('tomorrow noon'); echo $real->format(\DateTime::ATOM);

Во второй строке листинга 2 я с помощью директивы include включаю файл (listing1.php), начинающийся с директивы namespace. После этого я могу ссылаться на свои классы и функции, снабдив их префиксом с пространством имен zebra\. Я могу также по-прежнему использовать классы глобального уровня, такие как исходный класс DateTime, вставив в начале символ "\", чтобы тем самым обозначить глобальное пространство имен.

Технические приемы в листинге 2 достаточно удобны, однако есть способ сделать этот код еще проще: новое ключевое слово use. Это ключевое слово констатирует, что вы хотите иметь прямой доступ к определенному классу изнутри данного пространства имен (см. листинг 3).

Листинг 3. Включение пространства имен с помощью ключевого слова use
 <?php include 'listing1.php'; use zebra\DateTime; // Использование нашего собственного класса DateTime: $dt = new DateTime();

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

Листинг 4. Создание псевдонима
 <?php include 'listing1.php'; use zebra\DateTime as ZDT; // Использование нашего собственного класса DateTimes: $dt = new ZDT();

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

Трейты

Объектно-ориентированное программирование традиционно базируется на концепции классов и объектов, наследующих друг от друга. Разработчик начинает с абстрактной концепции, а затем шаг за шагом создает подклассы с потомками по мере углубления детализации. Если разработчику нужен единообразный API-интерфейс между объектами, концепция интерфейсов позволяет ему задать методы, которые должен реализовать соответствующий объект. Но что если разработчик хочет не просто декларировать (declare), какие методы должны существовать, но и одновременно с этим предоставить их реализации? В этом случае на помощь приходят т. н. трейты (trait).

Трейты, впервые реализованные в версии PHP 5.4 – это средство для горизонтального повторного использования кода (тогда как наследование – это вертикальное повторное использование кода). В других языках это средство иногда называется миксинами (mixin). В любом случае эта концепция является достаточно простой. Трейт или миксин – это способ единомоментного создания произвольного количества методов. Предположим, что у разработчика имеются некоторые общие методы для фильтрации и манипулирования данными или бизнес-логикой, которые должны совместно использоваться некоторыми объектами. Разработчик может сохранить эти методы в трейте, а затем повторно использовать их в любом классе по своему усмотрению.

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

Листинг 5. Декларирование и использование трейта
 <?php trait logging { private static $LOG_ERROR = 100; private static $LOG_WARNING = 50; private static $LOG_NOTICE = 10; protected $log_location = 'output.log'; protected function log($level, $msg) { $output = []; $output[] = "Class: ".__CLASS__.' | '; $output[] = "Level: {$level} | "; $output = array_merge($output, (array)$msg, ["\n"]); file_put_contents($this->log_location, $output, FILE_APPEND); } } class User { use logging; public function __construct() { $this->log(self::$LOG_NOTICE, 'New User Created'); } } class DB { use logging; private $db = 'localhost:8080'; public function connect() { // ... attempt to connect and fail: $this->log(self::$LOG_ERROR, ['Connection Failed-', $this->db]); } }

В Листинг 5 декларация trait logging начинается в строке 2. Обратите внимание, что трейт содержит метод, а также несколько свойств (в том числе статических). При поверхностном взгляде эта декларация выглядит как декларация класса, однако в ней используется ключевое слово trait.

Далее в листинге 5 я снова использую ключевое слово use, чтобы внедрить трейт в классы User и DB. Директива use logging; над определениями классов по существу естественным образом вытягивает в эти классы все свойства и методы из трейта logging. Теперь каждый класс имеет прямой доступ ко всем инструментам журналирования без необходимости их отдельной реализации. Магическая переменная __CLASS__, используемая внутри трейта, в этот момент с помощью трейта становится именем класса, что позволяет мгновенно настроить сообщения журнала в соответствии с классом.

Замыкания (альтернативное название: анонимные функции)

В более старых версиях PHP разработчик мог создавать функции программно посредством create_function, а также допускалось обходное решение для передачи функций: отправка имени функции в виде строки и последующий вызов функции посредством call_user_func иcall_user_func_array. Этому подходу не хватало элегантности подлинных анонимных функций, которые можно передавать между методами и классами или сохранять в переменных с соответствующей областью действия.

Анонимные функции безраздельно господствуют в языке JavaScript, а PHP-программисты, не знакомые с JavaScript, встречаются нечасто. Таким образом, включение анонимных функций стало естественным развитием языка PHP. Начиная с версии PHP 5.3 можно применять обычный синтаксис вида "функция-декларация" в любом месте, где можно использовать переменную (для хранения или передачи).

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

Листинг 6. Старый способ передачи функций
 <?php $insurees = [ 'u4937' => ['name' => 'Thomas Smythe', 'age' => 33], 'u1282' => ['name' => 'Gayle Runecor', 'age' => 25], 'u9275' => ['name' => 'Sara Pinnicle', 'age' => 57], 'u2078' => ['name' => 'Delilah Shock', 'age' => 41], ]; function insuree_age_sort($a, $b) { if ($a['age'] == $b['age']) { return 0; } return ($a['age'] > $b['age']) ? -1 : 1; } uasort($insurees, 'insuree_age_sort');

Код в листинге 6 выглядит несколько тяжеловесным из-за необходимости определения функции в той же области, а затем ее использования — даже если разработчик никогда не будет использовать ее снова. Благодаря т. н. замыканиям (closure) разработчик теперь может непосредственно создать и использовать функцию за один шаг. В листинге 7 показан пример этого гораздо более элегантного решения.

Листинг 7. Использование анонимной функции для сортировки
 <?php uasort($insurees, function ($a, $b) { if ($a['age'] == $b['age']) { return 0; } return ($a['age'] > $b['age']) ? -1 : 1; });

Однако никого не заставляют утверждать, что этот малозначительный вариант использования является единственным обоснованием необходимости данной возможности. Необходимо осознать, что именно происходит в этом месте. Функция, которую я создаю на лету для передачи в uasort(), является переменной первого класса. Функции можно хранить в переменных и передавать их в различные функции и классы. Реальная мощь замыканий становится очевидной, если посмотреть на возможность использования области действия, которая была добавлена в языке PHP вместе с замыканиями.

С помощью перегруженного ключевого слова use можно указать в текущей области конкретные переменные, к которым данная функция должна иметь доступ. Подобный способ позволяет манипулировать достаточно сложными деталями без необходимости постоянной передачи их в функцию при каждом обращении к этой функции из ее представления в виде переменной. Примеры (несколько искусственные) в листинге 8 и листинге 9 демонстрируют эту мощную возможность.

В листинге 8 область унаследованной переменной используется в обратном вызове.

Листинг 8. Использование области унаследованной переменной в обратном вызове
 <?php // Нахождение людей старше определенного возраста $minage = 40; $over = array_filter($insurees, function($a) use ($minage) { return ($a['age'] >= $minage); });

В листинге 9 используются замыкания с несколькими переменными и прямыми вызовами.

Листинг 9. Замыкания с несколькими переменными и прямыми вызовами
 <?php $urls = [ 'training' => '/training', 'magazine' => '/magazine', 't-shirt' => '/swag/tshirts', ]; $current = $_SERVER['REQUEST_URI']; // Может поступать из какого-либо другого места // Helper для ссылок; игнорирование ссылок, если мы находимся на данной странице: $link = function($name) use ($urls, $current) { if ($current == $urls[$name]) { return $name; } else { return "<a href=\"{$urls[$name]}\">{$name}</a>"; } }; ?> <p>Welcome to our website! Make sure to check out our <?= $link('training') ?> offerings, see the latest issue of our <?= $link('magazine'); ?>, and don't forget to check out our latest <?= $link('t-shirt') ?> designs as well.</p>

Если вы привыкли к работе с замыканиями в языке JavaScript, то вы уже знакомы с их мощными, гибкими, а иногда и опасными возможностями.

Генераторы

Первоначально версия PHP 5.0 поставлялась с исходным вариантом библиотеки SPL (Standard PHP Library). Библиотека SPL должна была служить набором стандартизованных способов для решения определенных проблем программирования, таких как создание очередей и связанных списков (и для предоставления расширяемых функций, таких как автозагрузчик файла класса).

Одно из средств, включенных в библиотеку SPL, носит название итератор (iterator). Iterator – это интерфейс (с коллекцией заранее собранных классов), который позволяет наделить любой класс способностью к циклическому итерированию (как будто это массив) с помощью ключевого слова foreach. Это удивительное изобретение позволяет единообразно проходить по всем "спискам вещей". Однако Iterator – это довольно сложная система, которая вынуждает разработчика создать класс и определить четыре метода. Иногда разработчику требуются возможности стандартного цикла foreach, но совершенно не нужны издержки на структуру класса для реализации этих возможностей.

Более современное средство под названием генератор (generator) позволяет — при посредстве ключевого слова yield— создать функцию, которая генерирует список значений и выдает их по одному. По существу, вместо возвращения одного значения разработчик собирает (yield) нужное количество значений. Затем к этой функции можно применить цикл foreach, чтобы извлечь все значения, которые функция хочет возвратить обратно.

В листинге 10 показан простой пример функции для разделения диапазона значений на равные части и возвращения этих частей.

Листинг 10. Генератор для разделения на части
 <?php function parts($start, $end, $parts) { // Нахождение фактической длины: $length = $end - $start; do { $start += $length / $parts; yield $start; } while ($start < $end); } // Деление 5 футов на 3 части: foreach (parts(0, 5, 3) as $l) { echo $l, " "; } echo "\n"; // Деление диапазона 10-90 на 2 части: foreach (parts(10, 90, 12) as $l) { echo $l, " "; } echo "\n";

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

Очевидно, это довольно искусственный пример, но тем не менее данный способ вполне можно применить к результатам запроса к базе данных или к результатам разбора XML-файла. Можно даже получать ключи и значения, чтобы непосредственно имитировать массив с помощью синтаксиса yield $key => $value, как показано в основанном на XML примере в листинге 11.

Листинг 11. Использование генератора для обработки XML
 <?php $xml = <<<EOXML <?xml version="1.0" encoding="UTF-8" ?> <products> <books> <book isbn="978-1940111001">Mastering the SPL Library</book> <book isbn="978-1940111056">Functional Programming in PHP</book> <book isbn="978-0981034508">Guide to Date and Time Programming</book> <book isbn="0973589825">Guide to PHP Design Patterns</book> </books> </products> EOXML; $books = function () use ($xml) { $products = simplexml_load_string($xml); foreach ($products->books->book as $book) { yield $book['isbn'] => (string)$book; } }; foreach ($books() as $isbn => $title) { echo "{$isbn}: {$title}\n"; }

И многое другое ...

Новые средства PHP слишком многочисленны, чтобы подробно рассмотреть их в этой статье. В таблице 1 приведен краткий список некоторых других важных дополнений, реализованных за несколько последних лет.

Таблица 1. Другие новые возможности языка PHP
ВозможностиВерсияОписание
Позднее статическое связывание (Late static binding) 5.3Возможность родительского класса вызвать статический метод или свойство, определенное одним из его потомков/наследников (в противоположность тому, как это делается обычно). Например, в родительском классе допускается существование универсальной функциональности, которая принимает конфигурации от своих расширенных потомков.
Nowdoc-синтаксис 5.3Возможность определения строки как блока текста без интерпретируемых переменных внутри него.
Ярлык трехчленного оператора (?:) 5.3Возможность опустить среднюю часть стандартного трехчленного оператора, чтобы при значении условия true по умолчанию обратно передавалось исходное сравниваемое значение. Пример: $result = $value ?: 0;
Метки перехода (goto) 5.3Хотя некоторые считают, что поддержка оператора goto не способствует развитию языка, этот оператор был добавлен, чтобы упростить реализацию на PHP некоторых задач программирования, таких как создание конечных автоматов.
Магические методы
__callStatic
__invoke
__debugInfo

5.3
5.3
5.6
Начиная с версии PHP 5.0 к существующим магическим методам были добавлены эти три новых метода, дополняющие мощный набор возможностей проектирования классов. Теперь разработчик может применять перегруженные и неопределенные статические методы, вызывать свой объект как функцию и управлять выводимой информацией при отладке объекта.
Постоянно включенный сокращенный код (<?=) 5.4Ранее, если разработчик деактивировал ярлыки в PHP, все изменения отключались. В версии PHP 5.4 программисту всегда доступен сокращенный код <?=, который широко используется при работе с шаблонами.
Сокращенный синтаксис массива 5.4Вместо декларирования массивов в виде array(1,2,3), теперь можно использовать скобки, например: [1,2,3]
Встроенный веб-сервер 5.4Среда исполнения PHP теперь поставляется со встроенным веб-сервером, что существенно упрощает разработку и тестирование простого кода, позволяя обойтись без конфигурирования веб-сервера Apache или IIS.

Заключение

Современная разработка на языке PHP совершенно не похожа на процедурный код минувших дней. При этом PHP продолжает сохранять высокий темп развития. На горизонте уже появилась версия PHP 7 –ее выпуск запланирован на конец 2015 года.

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


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура, Open source
ArticleID=1023840
ArticleTitle=Обновленный PHP: Новое лицо PHP
publish-date=12112015