Обзор нововведений PHP 5.4
В конце января команда разработчиков PHP выпустила версию PHP 5.4 RC 6. Это означает, что уже очень скоро свет увидит стабильная версия, а стало быть, самое время ознакомиться с нововведениями. Самым заметным и принципиальным нововведением этой версии являются трейты (traits) – механизм повторного использования кода. Но обо всем по порядку.
Чего не стало
В версии 5.4 PHP наконец-то избавился от идиотского режима Safe Mode. Кроме того, в лету канули ini директивы register_globals, register_long_arrays, allow_call_time_pass_reference, magic_quotes_gpc, maig_quotes_runtime и magic_quotes_sybase. Функции get_magic_quotes_gpc() и get_magic_quotes_runtime() всегда возвращают false. Функция set_magic_quotes_runtime() вызывает ошибку уровня E_CORE_ERROR. Удалены функции import_request_variables(), session_is_registered(), session_register() и session_unregister().
Что изменилось
Конструкция <?= теперь доступна всегда и не зависит от ini директивы short_open_tag. Напомню, эта конструкция равнозначна конструкции <?php echo. Значение по умолчанию для ini директивы default_charset теперь — UTF-8 (было ISO-8859-1), а для date.timezone – UTC (ранее PHP пытался определить временную зону автоматически). Уровень ошибки E_ALL теперь содержит уровень E_STRICT.
Что нового
Добавлена функция http_response_code(), позволяющая определить либо установить HTTP-код страницы. Ранее для установки статуса приходилось вызывать функцию header().
echo http_response_code(); // 200
http_response_code(404);
echo http_response_code(); // 404
Добавлена функция header_register_callback(), позволяющая задать функцию, выполняемую непосредственно перед отправкой заголовков:
header_register_callback(function() {
header_remove('Content-Type');
header('Content-Type: text/plain');
});
header('Content-Type: text/html');
echo 'Hello world!';
Такой код выведет текст со значением text/plain для заголовка Content-Type.
Добавлен интерфейс JsonSerializable. Он позволяет классу, использующему его, задавать, какие данные будут преобразованы в JSON функцией json_encode(). К сожалению, в данный момент документации по этому интерфейсу практически нет.
Нововведения в синтаксисе
В PHP 5.4 добавлен новый, короткий синтаксис для создания массивов:
$array = [1, 2, 3];
$assoc = ['foo' => 'bar', 'baz'];
Добавлена поддержка выражений при обращении к членам статических классов:
class Foo {
public static $barbar = 'bar';
public static function barbaz() {
return 'baz';
}
}
$bar = 'bar';
$baz = 'baz';
echo Foo::{$bar . $bar}; // 'bar'
echo Foo::{$bar . $baz}(); // 'baz'
Добавлена поддержка обращения к членам класса при инстанциировании:
class Foo {
public function setBar($v) {
$this->bar = $v;
return $this;
}
}
$foo = (new Foo)->setBar('bar');
echo $foo->bar; // 'bar'
Трейты
Самое главное и вкусное нововведение PHP 5.4 – трейты. Трейты – это наборы методов, которые можно подключить к произвольному классу, не затрагивая его цепь наследования. Рассмотрим область применения на примере: у нас есть класс Person и наследуемые от него классы John, Jack и Jill. John умеет плавать, Jack умеет петь, а Jill умеет и плавать и петь одновременно. Классической моделью наследования выразить это не просто, но с трейтами все элементарно:
abstract class Person {
public $name;
public function greet() {
echo "Hi, I’m " . $this->name;
}
}
trait Swimmer {
public function swim() {
echo $this->name . ' started swimming';
}
}
trait Singer {
public function sing() {
echo $this->name . ' started singing';
}
}
class John extends Person {
use Swimmer;
public $name = 'John';
}
class Jack extends Person {
use Singer;
public $name = 'Jack';
}
class Jill extends Person {
use Swimmer, Singer;
public $name = 'Jill';
}
$john = new John;
$jack = new Jack;
$jill = new Jill;
$john->greet(); // Hi, I'm John
$john->swim(); // John started swimming
$jack->greet(); // Hi, I'm Jack
$jack->sing(); // Jack started singing
$jill->greet(); // Hi, I'm Jill
$jill->swim(); // Jill started swimming
$jill->sing(); // Jill started singing
Как видим, трейты позволяют очень гибко компоновать функционал классов. Трейты могут содержать методы, свойства, а также другие трейты. Трейты могут содержать абстрактные и статические методы, но не статические свойства. У трейтов есть механизм разрешения конфликтов, однако он не совершенен. Например, если в классе, использующем трейт, есть свойство с таким же именем, что и свойство в трейте, то возникнет ошибка. Если значение и видимость свойств совпадает, то ошибка будет уровня E_STRICT, в противном случае возникнет фатальная ошибка.
С разрешением конфликтов в методах все намного лучше. Методу, унаследованному из базового класса, будет предпочтен метод из трейта:
class Base {
public function sayHello() {
echo 'Hello ';
}
}
trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base {
use SayWorld;
}
$o = new MyHelloWorld;
$o->sayHello(); // 'Hello World!'
Методу, внесенному трейтом, будет предпочтен метод из текущего класса:
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough;
$o->sayHello(); // 'Hello Universe!'
Если класс использует более одного трейта и имена одного из методов этих трейтов совпадают, то конфликт необходимо решить самостоятельно:
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
Здесь видим два ключевых слова: insteadof и as. Первое позволяет определить приоритет методов в двух трейтах с одинаковыми методами. Второе же предоставляет гораздо более интересные возможности: помимо изменения названия метода трейта, используя ключевое слово as, мы можем изменить область видимости метода:
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// Изменим область видимости метода sayHello
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// Изменяя и название и область видимости метода,
// мы вводим алиас метода sayHello, не удаляя последний
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
Вдобавок ко всему этому в PHP 5.4 появились функции работы с трейтами: trait_exists() и get_declared_traits().
На этом тема трейтов, конечно же, не исчерпана, лично мне еще многое в них не понятно, так как документация по этому вопросу далеко не полная, да и PHP еще не в своей финальной версии. Остается только ждать.
Заключение
В данной статье я описал далеко не все изменения, но только самые интересные и существенные на мой взгляд. За бортом остались бесчисленные багфиксы и специфические для стандартных расширений нововведения. С полным списоком изменений вы можете ознакомиться на официальном сайте проекта PHP. Желаю всем приятной работы!
