Что нового в PHP 5.3

В PHP 5.3, как и во всей пятой ветке PHP, включена новая машина-интерпретатор скриптов Zend Engine 2.0. Благодаря этому PHP стал работать быстрее примерно на 15-20%.

?: — сокращение тернарного оператора

С PHP 5.3 стало возможным не писать среднюю часть тернарного оператора. Выражение expr1 ?: expr3 возвращает expr1 если expr1 не пустой, и expr3 в противном случае.

Тернарный — состоящий из трёх частей, компонентов.

$a = $expr1 ?: $expr3;
// равносильно записи:
$a = $expr1 ? $expr1 : $expr3;

Пример тернарного оператора:

// полная запись
if ( $a > 100 )
	$result = "Больше";
else
	$result = "Меньше";

// краткая запись
$result = $a > 100 ? "Больше" : "Меньше";

В короткой записи есть еще момент производительности, например:

// полная запись
if ( get_post_meta(25, 'meta_key', 1) )
	echo esc_html( get_post_meta(25, 'meta_key', 1) );
else
	echo 'Мета поля нет';

// короткая запись
echo esc_html( get_post_meta(25, 'meta_key', 1) ?: 'Мета поля нет' );

В полной записи функция get_post_meta() вызывается 2 раза. В короткой один раз, и если она что-то вернула, второму аргументу тернарного оператора сразу передается полученное значение: не нужны дополнительные переменные...

$func = function() use (){ } — анонимные (лямбда) функции

Лямбда-функции еще называют «анонимными функциями», потому что для них не указывается название.

Лямбда-функции представляют собой замыкание (англ. closure) — это особый вид функции, которая определена в теле другой функции и создаётся каждый раз во время её выполнения. Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. Насколько я понял, любая функция — это замыкание текущего контекста, т.е. контекст не будет очищен пока работает функция. Но если в функции есть лямбда-функция, то она становится замыканием, и если в неё передаются переменные из «верхней» функции, то они не будут очищены, до тех пор пока работает вложенная-функция, даже если «верхняя» функция работу закончила...

В ранних версиях, анонимные функции создавались с помощью функции create_function().

Пример создания анонимной функции для сортировки usort():

$arr = array(3, 2, 5, 6, 1);

usort( $arr, function($a, $b) {
	if ( $a == $b )
		return 0;

	return ( $a > $b ) ? -1 : 1;
});

Еще одна фишка лямбда-функций — это лексическое связывание (использование переменных из текущей области видимости) с помощью оператора use:

$var = 'Превед, Медвед!';
$func = function() use ( $var ) { echo $var; };
$func(); //> Превед, Медвед!

Переменные передаются как значение, но можно передать и ссылку на переменную, указав &:

$var = 'Превед, Медвед!';
$func = function() use ( & $var ) { $var = $var .' Мы в гости!'; };
$func(); // вызовем
echo $var; //> Превед, Медвед! Мы в гости!

method()->var — получение объекта из метода/функции

Это удобно:

$object->method()->method()->method();
$object->method()->method()->member = 5;

В PHP ниже 5.3 писали как-то так:

$tmp = & $object->method();
$tmp = & $tmp->method();
$tmp->method();

<<<'DOC' — поддержка NOWDOC

В php 5.3 можно использовать аналог HEREDOC, который называется NOWDOC. Особенность его в том, что внутри него переменные остаются простым текстом, как если бы мы указали её в строке с одинарными кавычками: 'текст $foo':

$foo = 'Лето';

// HEREDOC был в 5.2
$str = <<<DOC
	Текст с переменной '$foo'
DOC;

echo $str; // Текст с переменной 'Лето'

// NOWDOC появился в 5.3
$str = <<<'DOC'
	Текст с переменной '. $foo .'
DOC;

echo $str; // Текст с переменной '. $foo .'

namespace — поддержка пространств имен

Пространства имен нужны, чтобы избежать конфликтов при совпадении названий функций/классов/переменных/констант. Если коротко: одинаковые называния в разных пространствах — это разные названия.

Пример ниже должен объяснить почти все, что возможно в пространствах имен. За подробностями идем в официальную документацию.

<?php
#
# Объявлять пространство нужно в самом начале файла содержащего пространство имен,
# т.е. до любого кода, кроме зарезервированного declare(encoding='...');.
# Также ничего не должно выводиться на экран до объявления пространства
# Одно и тоже пространство имен можно определять в разных файлах. Так эти файлы будут относиться к одному пространству

# Объявляем пространство my\name
namespace my\name;

// Динамичное получения названия пространства --------------
$s = __NAMESPACE__; //> my\name
$s = __NAMESPACE__ . '\HELLO'; //> my\name\HELLO
// namespace: есть еще специальное слово namespace, которое используется для динамичного
// получения названия текущего пространства при вызове функций/методов/констант (см. ниже)

// ГЛОБАЛЬНЫЕ функции/классы/константы в нашем пространстве --------------
$s = strlen('hi');           // вызовет my\name\strlen() - если функция есть в нашем пространстве, иначе глобальную функцию strlen()
define('HELLO', 'HI всем');  // добавит константу в глобальное пространство "\HELLO"

# Доступ к глобальным классам/функциям/константам из пространства имен
$a = \strlen('hi'); // вызывает глобальную функцию strlen()
$b = \ABSPATH;      // получает доступ к глобальной константе ABSPATH
$c = new \WP_Query; // создает экземпляр глобального класса WP_Query

// ФУНКЦИЯ в нашем пространстве --------------
function my_func(){ return 'Моя функция'; }

// Вызов
my_func();           //> "Моя функция"
namespace\my_func(); //> "Моя функция"
\my\name\my_func();  //> "Моя функция"
// my\name\my_func();   //> ошибка: будет вызвана функция my\name\my\name\my_func()
						// такой синтаксис можно использовать для доступа к под-пространствам нашего пространства

// ФУНКЦИЯ в нашем пространстве, которая существует в глобальном --------------
function trim( $str ){
	return \trim( $str, '-' ); # если вызвать trim( $str, '-' ), то функция вызовет сама себя...
}

// Вызов
$s = trim('-foo');           // вызов trim() из текущего пространства. Выведет: foo
$s = \my\name\trim('-foo');  // тоже что в строке выше
$s = namespace\trim('-foo'); // тоже что в строке выше

$s = \trim('-foo'); // вызов trim() из глобального пространства. Выведет: -foo

// КОНСТАНТЫ в нашем пространстве --------------
const HELLO = 'HI';                     // добавим константу в текущее пространство
define('my\name\HELLO', 'HI');          // тоже что в строке выше
define(__NAMESPACE__ . '\HELLO', 'HI'); // тоже что в строке выше

// Вызов
$s = HELLO;           //> HI - если константа есть в текущем пространстве, или значение глобальной константы
$s = \my\name\HELLO;  //> HI
$s = namespace\HELLO; //> HI
$s = \HELLO;          //> HI всем - глобальная константа HELLO

// КЛАСС в нашем пространстве --------------
class MyClass {
	function method(){ return 'метод MyClass'; }
	static function static_method(){ return 'статический метод MyClass'; }
}

// Вызов
$a = new MyClass;            // обращение к MyClass из текущего пространства
$a = new \my\name\MyClass;   // тоже что в строке выше

$s = namespace\MyClass::static_method(); //> 'статический метод MyClass' - вызывает статический метод "static_method" класса my\name\MyClass.
$s = $a::static_method();                // тоже что в строке выше

$s = $a->method();                       //> 'метод MyClass' - вызывает метод "method" класса my\name\MyClass
										 // namespace\MyClass->method() - такой вызов метода, вызовет ошибку - syntax error

// ВНЕДРЕНИЕ функций/методов/констант в наше пространстве из других пространств --------------

// ЗАМЕТКА: операторы use можно комбинировать: указывать через запятую
// Например: use other\name\OtherClass as Another, other\name\NSname;

use other\name\OtherClass as Another;
$obj = new Another; // создает объект класса other\name\OtherClass

use other\name; // теперь name = other\name
name\other_func(); // вызывает функцию other\name\other_func();

// импорт глобального класса
use WP_Query;
$a = new WP_Query(); // создаст экземпляр класса WP_Query
					 // без выражения "use WP_Query;" создавался бы экземпляр my\name\WP_Query

// импорт функции (PHP 5.6+)
use function other\name\other_func;
$s = other_func(); //> "Другая Функция" - работа функции other\name\other_func()

// импорт функции под псевдонимом func (PHP 5.6+)
use function other\name\other_func as func;
$s = func();         //> "Другая Функция" - работа функции other\name\other_func()

//const other\name\HELLO2 = 'И снова здрасте!'; // вызовет ошибку синтаксиса, что странно
define('other\name\HELLO2', 'И снова здрасте!');

// импорт константы (PHP 5.6+)
use const other\name\HELLO2;
$s = HELLO2; //> "И снова здрасте!" - содержимое константы other\name\HELLO2

// ЕЩЕ ОДНО ПРОСТРАНСТВО в одном файле --------------
// Подробнее: http://php.net/manual/ru/language.namespaces.definitionmultiple.php

namespace other\name;

class OtherClass {}
function other_func() { return 'Другая Функция'; }

// При описании нескольких пространств в одном файле лучше использовать синтаксис со скобками:
/*
namespace MyProject {
	function connect() {}
}

namespace AnotherProject {
	function connect() {}
}
*/

__DIR__ — новая магическая константа

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

__DIR__ можно заменить:

dirname(__FILE__)

$class::$foo — динамичное указание класса

Это дает динамичный доступ к статическим методам/свойствам класса:

class C {
	static $foo = 'foo';
}

$class = 'C';
echo $class::$foo; //> foo

const — ключевое слово для создания констант вне классов

Сразу пример, где все понятно:

define('SHORTINIT', 'true');

// теперь можно объявить константу и так:
const SHORTINIT = 'true';

В отличие define(), такие константы, должны быть объявлены в самой верхней области видимости, потому что они определяются при компилировании скрипта. Это значит, что их нельзя объявлять внутри функций/циклов/выражений if или try/ catch блоков.

static::method() — статическое связывание

Статическое объявление метода/свойства связывает его с классом из которого оно вызывается, а не с тем в котором оно зарегистрировано. Посмотрим на примере:

class A {
	static function who() {
		echo __CLASS__;
	}

	static function test1() {
		self::who();
	}

	static function test2() {
		static::who(); // статическое связывание
	}
}

class B extends A {
	static function who() {
		echo __CLASS__;
	}
}

echo B::test1(); //> A
echo B::test2(); //> B
echo B::who(); //> B

Подробнее про статическое связывание читайте в документации.

goto hell; — оператор goto

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

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

Также нельзя перейти внутрь любой циклической структуры или оператора switch. Но можно выйти из любой циклической структуры, поэтому «goto» удобен как замена многоуровневых break.

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

function zayac(){
	$i = 1;
	$out = '';
	start: $out .= ($i > 1 ? '-' : '' ) .$i;

	if( $i++ < 5 ){ goto start; }

	return $out . ' вышел зайчик погулять';
}

echo zayac(); //> 1-2-3-4-5 вышел зайчик погулять

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

for( $i=0, $j=50; $i<100; $i++ ) {
	while( $j-- ) {
		if( $j==17 ) goto end;
	}
}
echo "i = $i"; // будет пропущено

end: echo 'j дошло до 17';

__callStatic(), __invoke() — магические методы

__callStatic() — срабатывает, когда вызывается несуществующий метод из статического контекста: Foo::bar():

class A {
	static function __callStatic( $name, $args ){
		return $name .' '. print_r( $args, 1 );
	}
}
echo A::no_matter_what('bar');
/* Выведет:
no_matter_what Array
(
	[0] => bar
)
*/

__invoke() — срабатывает, когда объект выполняется как функция: $obj():

class A {
	function __invoke( $var ){
		var_dump( $var );
	}
}
$obj = new A;
$obj('foo'); //> string(3) "foo"

Эта заметка встроена в: PHP 5.3 - 8.2 — Синтаксис, Новинки