PHP 8 — пробуем новые возможности

от автора

PHP 8 уже на этапе release candidate, версия RC 3 вышла 29 октября, а полноценный релиз назначен на 26 ноября. Так что пора взглянуть на новые возможности, которые нас ждут в PHP 8. График релизов можно посмотреть здесь. А официальное руководство по обновлению на новую версию лежит тут.

Добавлена поддержка типов union (RFC)

Тип union принимает значения разных других типов, а не только какого-то одного.

<?php declare(strict_types=1);   class Number {     private int|float $number;       public function setNumber(int|float $number): void {         $this->number = $number;     }       public function getNumber(): int|float {         return $this->number;     } }   /**  * We can pass both floats or integer values  * to the number object. Try passing a string.  */ $number = new Number();   $number->setNumber(5);   dump($number->getNumber());   $number->setNumber(11.54);   dump($number->getNumber());   exit; 

Добавлены WeakMap (RFC)

Слабые карты (weak maps) позволяют создавать связи между объектами и произвольными значениями (как и SplObjectStorage), при этом объекты, используемые в качестве ключей, не защищаются от сборщика мусора. Если сборщик уничтожает такой объект, тот просто удаляется из карты.

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

Если вы сделаете то же самое с массивом, то ссылки на объект сохранятся, что приведёт к утечке памяти.

<?php declare(strict_types=1);   class FooBar {     public WeakMap $cache;          public function __construct() {       $this->cache = new WeakMap();     }       public function getSomethingWithCaching(object $obj) {         return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj);     }          public function computeSomethingExpensive(object $obj) {     dump("I got called");     return rand(1, 100);     } }   $cacheObject = new stdClass;   $obj = new FooBar; // "I got called" only will be printed once $obj->getSomethingWithCaching($cacheObject); $obj->getSomethingWithCaching($cacheObject);   dump(count($obj->cache));   // When unsetting our object, the WeakMap frees up memory unset($cacheObject);   dump(count($obj->cache));   exit; 

Новое исключение ValueError

В PHP 8 появился новый встроенный класс исключений ValueError. Он дополняет собой \Exception. PHP бросает такое исключение каждый раз, когда вы передаёте функции значение правильного типа, однако его нельзя использовать в этой операции. Раньше в таких случаях выдавалось предупреждение. Примеры:

<?php declare(strict_types=1);   /**  * We pass an array to array_rand,  * which is of the correct type. But  * array_rand expects non-empty arrays.  *  * This throws a ValueError exception.  */ array_rand([], 0);   /**  * The depth argument for json_decode is a  * valid integer, but it must be greater than 0  */ json_decode('{}', true, -1); 

При определении функций можно использовать вариативный аргумент

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

<?php declare(strict_types=1);   class A {     public function method(int $many, string $parameters, $here) {       } } class B extends A {     public function method(...$everything) {         dd($everything);     } }   $b = new B(); $b->method('i can be overwritten!'); exit; 

Возвращаемый тип static (RFC)

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

<?php declare(strict_types=1);   class Test {     public function doWhatever(): static {         // Do whatever.         return $this;     } }   exit; 

Литерал имени класса для объекта (RFC)

Теперь можно извлекать имя класса объекта с помощью $object::class. Результат будет такой же, как в случае с get_class($object).

<?php declare(strict_types=1);   auth()->loginUsingId(1);   dump(auth()->user()::class);   // Or with a temporary variable $user = auth()->user();   dump($user::class); exit; 

Настройки синтаксиса переменных (RFC)

New и instanceof теперь можно использовать с произвольными выражениями: new (выражение)(...$args) и $obj instanceof (выражение).

<?php declare(strict_types=1);   class Foo {} class Bar {}     $class = new (collect(['Foo', 'Bar'])->random());   dd($class);   exit; 

Интерфейс Stringable (RFC)

В PHP 8 появился новый интерфейс Stringable, который добавляется автоматически, как только класс реализует метод __toString. Вам не нужно явно реализовывать этот интерфейс.

<?php declare(strict_types=1);   class Foo {     public function __toString() {         return 'I am a class';     } }   $obj = new Foo; dump($obj instanceof Stringable);   exit; 

Теперь трейты могут определять абстрактные приватные методы (RFC)

<?php declare(strict_types=1);     trait MyTrait {     abstract private function neededByTheTrait(): string;       public function doSomething() {         return strlen($this->neededByTheTrait());     } }   class TraitUser {     use MyTrait;       // This is allowed:     private function neededByTheTrait(): string { }       // This is forbidden (incorrect return type)     // private function neededByTheTrait(): stdClass { }       // This is forbidden (non-static changed to static)     // private static function neededByTheTrait(): string { } }   exit; 

throw теперь можно использовать как выражение (RFC)

Выражение throw теперь можно использовать там, где допускаются только выражения: в стрелочных функциях, coalesce-операторах тернарных условных операторах (ternary/elvis).

<?php declare(strict_types=1);   $callable = fn() => throw new Exception();   $nullableValue = null;   // $value is non-nullable. $value = $nullableValue ?? throw new \InvalidArgumentException();     exit; 

В параметрах списка теперь допускается опциональная висящая запятая (RFC)

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

<?php declare(strict_types=1);   function method_with_many_arguments(     $a,      $b,     $c,     $d, ) {     dump("this is valid syntax"); }   method_with_many_arguments(     1,     2,     3,     4, );   exit; 

Ловля исключений без сохранения в переменной (RFC)

Теперь можно писать catch (исключение) для ловли исключений без их сохранения в переменной.

<?php declare(strict_types=1);   $nullableValue = null;   try {     $value = $nullableValue ?? throw new \InvalidArgumentException(); } catch (\InvalidArgumentException) {     dump("Something went wrong"); }     exit; 

Добавлена поддержка типа mixed (RFC)

В PHP 8 появился новый тип mixed. Он может быть эквивалентен типам array, bool, callable, int, float, null, object, resource, string.

<?php declare(strict_types=1);   function debug_function(mixed ...$data) {     dump($data); }   debug_function(1, 'string', []);   exit; 

Добавлена поддержка атрибутов

Есть несколько предложений по внедрению атрибутов в PHP 8:

Это одно из крупнейших изменений в PHP 8. Возможно, сначала с ними не так легко будет разобраться. Если кратко, то атрибуты позволяют добавлять метаданные в PHP-функции, параметры, классы и т.д. Эти метаданные можно потом программно извлекать. Если в PHP 7 или ниже вам потребуется парсить doclock`и, атрибуты помогут обратиться к этой информации, глубоко интегрированной в сам PHP.

Чтобы было понятнее, представим, что вашим пользователям нужно дать возможность добавлять промежуточное ПО в контроллер класса или метода с помощью использования атрибута.

<?php declare(strict_types=1); // First, we need to define the attribute. An Attribute itself is just a plain PHP class, that is annotated as an Attribute itself.   #[Attribute] class ApplyMiddleware {     public array $middleware = [];       public function __construct(...$middleware) {         $this->middleware = $middleware;     } }   // This adds the attribute to the MyController class, with the "auth" middleware as an argument.   #[ApplyMiddleware('auth')] class MyController {     public function index() {} }   // We can then retrieve all ApplyMiddleware attributes on our class using reflection // And read the given middleware arguments.   $reflectionClass = new ReflectionClass(MyController::class);   $attributes = $reflectionClass->getAttributes(ApplyMiddleware::class);   foreach ($attributes as $attribute) {     $middlewareAttribute = $attribute->newInstance();     dump($middlewareAttribute->middleware); }   exit; 

Добавлена поддержка продвижения свойств конструктора (RFC)

Предложено добавить простой синтаксис, позволяющий комбинировать конструктор с определением свойств:

<?php declare(strict_types=1);   class User {     public function __construct(         public int $id,         public string $name,     ) {} }   $user = new User(1, 'Marcel');   dump($user->id); dump($user->name);   exit; 

Добавлена поддержка выражения match (RFC)

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

<?php declare(strict_types=1);   echo match (1) {     0 => 'Foo',     1 => 'Bar',     2 => 'Baz', };   exit; 

Добавлена поддержка оператора nullsafe (?->) (RFC)

Когда результат левой части оператора равен null, исполнение всей цепочки останавливается, а её результат приравнивается к null. В противном случае цепочка ведёт себя как обычный оператор ->.

<?php declare(strict_types=1);   class User {     public function getAddress() {} }   $user = new User();   $country = $user?->getAddress()?->country?->iso_code;   dump($country);   exit; 

Добавлена поддержка именованных аргументов (RFC)

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

<?php declare(strict_types=1);   array_fill(start_index: 0, num: 100, value: 50);   exit; 

ссылка на оригинал статьи https://habr.com/ru/company/mailru/blog/525614/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *