PHP постоянно развивается, и только что мир увидело их последнее обновление — PHP 7.4. Производительность и скорость продолжают расти, в чём мы убедились ещё в предыдущих релизах PHP 7. Предварительная загрузка — одно из самых ярких обновлений. Эта возможность позволяет значительно ускорить выполнение скрипта и делает код чище и быстрее благодаря уменьшённому количеству строк кода.


PHP является важным элементом всемирной паутины и используется более чем на 79% всех сайтов. Известные сайты, такие как Facebook, Википедия, WordPress и многие другие используют PHP.

Мы можем увидеть что скорость веб-сайтов WordPress увеличивается вдвое, если сравним сайты, работающие на PHP 5 и 7. От последней версии больше всего выиграют сайты на WordPress.





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

На следующем графике сравниваются общие результаты теста производительности 7.4 и более старых версий PHP.





PHP 7.4: Что Нового?


Постоянные ежегодные обновления PHP7 начали выходить с 2016 года: различные новые функции, возможность писать более чистый код, чтобы сделать язык более надёжным и удобным, если вы запускаете его на своём сайте и т.д. Давайте взглянем на некоторые модификации, которые появились с выходом версии PHP 7.4.

Полный список изменений можно посмотреть в их ченджлоге.


Предварительная Загрузка


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


Предварительная загрузка контролируется единственной новой директивой php.ini: opcache.preload. Используя эту директиву, мы можем указать один файл PHP — тот, который будет выполнять задачу предварительной загрузки. После загрузки этот файл полностью выполняется и может предварительно загружать другие файлы, либо включая их, либо используя функцию opcache_compile_file ().


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


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


Оператор Spread Вместо array_merge


PHP начал поддерживать распаковку аргументов (Оператор Spread), начиная с версии PHP 5.6. Теперь, с выходом 7.4, мы можем использовать оператор Spread в массивах. Синтаксис для распаковки массивов и Traversables в списки аргументов называется распаковкой аргументов. И чтобы это произошло, вам нужно всего лишь добавить три точки (…) в начале.


Взгляните на этот пример:


$tropicalfruits = ['banana', 'orange'];
$fruits = ['apple', 'pear', ...$tropicalfruits, 'plum'];
// [‘apple’, ‘pear’, ‘banana’, ‘orange’, ‘plum’];

Теперь мы также можем расширить массив другим массивом с любого места, используя синтаксис оператора Spread.


Более длинный пример:


$num1 = [1, 2, 3];
$num2 = [...$num1]; // [1, 2, 3]
$num3 = [0, ...$num1]; // [0, 1, 2, 3]
$num4 = array(...$num1, ...$num2, 111); // [1, 2, 3, 1, 2, 3, 111]
$num5 = [...$num1, ...$num1]; // [1, 2, 3, 1, 2, 3]

Кроме того, вы можете использовать его в функции. Как в этом примере:


function getNum() {
  return ['x', 'y'];
}
$num6 = [...getNum(), 'z']; // ['x', 'y', 'z']
 
$num7 = [...new NumIterator(['x', 'y', 'z'])]; // ['x', 'y', 'z']
 
function arrGen() {
    for($i = 11; $i < 15; $i++) {
        yield $i;
    }
}
$num8 = [...arrGen()]; // [11, 12, 13, 14]

Также вы можете распаковывать массивы и генераторы, которые возвращаются функцией, прямо в новый массив:


function getFruits(){
return ['apple', 'orange', 'banana'];
}
$num1 = [...getFruits(), 'plum', 'grapefruit', 'watermelon'];

PHP 7.4 выводит следующий массив:


array(6) {
[0]=>
string(3) "apple"
[1]=>
string(3) "orange"
[2]=>
string(8) "banana"
[3]=>
string(4) "plum"
[4]=>
string(5) "grapefruit"
[5]=>
string(7) "watermelon"
}

Используя это выражение массива, операторы спреда должны иметь лучшую производительность по сравнению с 7.3 array_merge. Это связано с тем, что оператор Spread является языковой структурой, а array_merge — функцией. Другая причина заключается том, что оператор спреда поддерживает реализацию объектов Traversable, в то время как array_merge поддерживает только массивы.


Ещё одна потрясающая и действительно удобная функция, которая появилась в 7.4 — это удаление индексированных массивов array_merge. Строковые ключи не поддерживаются. Больше никаких смещений индекса!


$array = [‘dog, ‘cat’];
$array[2] = ‘cat’;
$array[1] = ‘giraffe’; //shifting
var_dump($array);
// prints
array(3) {
[0]=>
string(6) "dog"
[1]=>
string(5) "giraffe"
[2]=>
string(6) "cat"

Ещё одним преимуществом PHP 7.4 является функциии-генераторы. Разница между генератором и обычной функцией заключается в том, что функция-генератор будет перебирать столько значений, сколько необходимо, вместо того, чтобы возвратить одно значение. Итак, любая функция, содержащая ключевое слово yield, является функцией-генератором:


function generator() {
for ($i = 3; $i <= 5; $i++) {
yield $i;
}
}
$num1 = [0, 1, 2, ...generator()];

Слабые ссылки


В PHP 7.4 есть класс WeakReference, который позволяет сохранить ссылку на объект, не препятствующую его уничтожению. Слабые ссылки полезны для реализации кеш-подобных структур.


Не путайте WeakReference с классом WeakRef или расширением Weakref.


WeakReference {
/* Methods */
public __construct ( void )
public static create ( object $referent ) : WeakReference
public get ( void ) : ?object
}

Контравариантные Параметры и Ковариантные Возвраты


На данный момент PHP в основном использует инвариантные типы параметров и возвращаемого значения. Это подразумевает, что подтип параметра или тип возвращаемого значения также должны быть типа X, если метод имеет параметр или тип возвращаемого значения типа X.


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


Давайте посмотрим на оба примера:


Ковариантный тип возвращаемых данных:


interface Factory {
function make(): object;
}
class UserFactory implements Factory {
function make(): User;
}

Контравариантный тип параметра:


interface Concatable {
function concat(Iterator $input);
}
class Collection implements Concatable {
// accepts all iterables, not just Iterator
function concat(iterable $input) {/* . . . */}
}

Типизированные Свойства 2.0


Функция контроля типов (type hints) появилась ещё в PHP 5. Она позволяет указать тип переменной, которая должна быть передана функции или методу класса.


Начиная с PHP 7.2 стало возможным использовать type hints с типом данных объекта, что вселило надежду получить ещё больше возможностей.


В PHP 7.4 стаёт возможной поддержка следующего списка типов:


bool, int, float, string, array, object, iterable, self, parent
any class or interface name
?type // where "type" may be any of the above

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


Поддерживаются все типы, кроме void и callable. Callable был удалён, потому что его поведение зависело от контекста; а void оказался недостаточно полезным, кроме того свою роль сыграла неясная семантика типа.


Давайте взглянем на пример ниже.


Класс PHP 7.3:



class User {
    /** @var int $id */
    private $id;
    /** @var string $name */
    private $name;
    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }
    public function getId(): int {
        return $this->id;
    }
    public function setId(int $id): void {
        $this->id = $id;
    }
    public function getName(): string {
        return $this->name;
    }
    public function setName(string $name): void {
        $this->name = $name;
    }
}

В PHP 7.4 классы выглядят намного проще:



class User {
    public int $id;
    public string $name;
    public function __construct(int $id, string $name) {
        $this->id = $id;
        $this->name = $name;
    }
}

Поддерживаемые типы PHP 7.4:


class Example {
  
    public int $scalarType;
    protected ClassName $classType;
    private ?ClassName $nullableClassType;
 
    // Types are also legal on static properties
    public static iterable $staticProp;
 
    // Types can also be used with the "var" notation
    var bool $flag;
 
    // Typed properties may have default values (more below)
    public string $str = "foo";
    public ?string $nullableStr = null;
 
    // The type applies to all properties in one declaration
    public float $x, $y;
    // equivalent to:
    public float $x;
    public float $y;
}

Стрелочные Функции 2.0


Не секрет, что анонимные функции в PHP слишком громоздкие и продолжительные, даже если речь идёт о выполнении самых простых действий. Поэтому из JavaScript были позаимствованы стрелочные функции, или короткие замыкания. Их задача оптимизировать код PHP, улучшить его читаемость.


Анонимные функции усложняют чтение кода с второстепенной функциональностью.


Вот пример анонимной функции в PHP 7.3:


function array_values_from_keys($arr, $keys) {
    return array_map(function ($x) use ($arr) { return $arr[$x]; }, $keys);
}

А это легко читаемая стрелочная функция в PHP 7.4:


function array_values_from_keys($arr, $keys) {
    return array_map(fn($x) => $arr[$x], $keys);
}

Следовательно, вот насколько простыми являются стрелочные функции:


fn(parameter_list) => expr

Вы видите две функции $fn1 и $fn2. Код выглядит иначе, но функции передают идентичный результат.


$c = 1;
$fn1 = fn($a) => $a + $c;
 
 
$fn2 = function ($a) use ($c)
{
    return $a + $c;
};

Это также наверняка будет работать с вложенными стрелочными функциями:


$c = 1;
$fn = fn($a) => fn($b) => $a * $b + $c;

Это что-то, что было за пределами возможностей версии 7.3. Родительская функция получает $c, в то время как дочерняя функция также получает $c от родительской функции. В PHP 7.4, внешний диапазон может стать доступным в дочерней функции.


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


fn(array $a) => $a;
fn(): int => $a;
fn($a = 42) => $a;
fn(&$a) => $a;
fn&($a) => $a;
fn($a, ...$rest) => $rest;

Также стоит отметить, что стрелочные функции имеют самый низкий приоритет:


fn($a) => $a + $b
// is
fn($a) => ($a + $b)
// not
(fn($a) => $a) + $b

Устаревшие Функции


На данный момент приоритет операторов “.”, “+” и “-” одинаков. Любая комбинация этих операторов будет решена слева направо.


Взгляните на пример кода PHP 7.3:


echo "sum: " . $x + $y;
// would be evaluated left-to-right
echo ("sum: " . $x) + $y;
// or like this

В PHP 7.4 сложения и вычитания всегда выполняются перед стрингой, поскольку “+” и “-” имеют приоритет над “.”:


echo «sum: ». $x + $y;
// would be performed as if the code were as below.
echo «sum :». ($x + $y);

Левоассоциативный Тернарный Оператор


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


Давайте посмотрим на следующий пример:


return $a == 1 ? 'one'
 	: $a == 2 ? 'two'
 	: $a == 3 ? 'three'
 	: $a == 4 ? 'four'
           	: 'other';

В большинстве других языков это читается так:


return $a == 1 ? 'one'
 	: ($a == 2 ? 'two'
 	: ($a == 3 ? 'three'
 	: ($a == 4 ? 'four'
           	: 'other')))

В то время как в PHP, это читается:


return ((($a == 1 ? 'one'
 	: $a == 2) ? 'two'
 	: $a == 3) ? 'three'
 	: $a == 4) ? 'four'
           	: 'other';

Поскольку часто это оказывается не тем, чем планировалось, в результате мы получим ошибки.


Выводы


PHP 7.4 представил новые удобные функции для каждого PHP-разработчика.


Сайты WordPress и их пользователи определённо выиграют от этих улучшений. Сокращение использования памяти и быстрое время выполнения — это лишь некоторые преимущества PHP 7.4 перед старыми релизами.


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