LINUX.ORG.RU

PHP 8.1

 


2

2

Состоялся релиз PHP 8.1 — языка программирования общего назначения, под управлением которого, согласно данным w3techs, работают порядка 78 % сайтов. Среди основных изменений в новой версии стоит отметить добавление перечислений, «зелёных» потоков и интерсекционных типов.

Новая функциональность

Enums

Добавлены перечисления, которые базируются на классах и объектах (поэтому оператор проверки типа instanceof работает и с ними). Перечисления вмещают ряд констант. Но что самое важное, перечисления создают отдельный тип, с которым уже можно работать.

Пример работы с перечислениями, взятый из RFC:

enum Suit { 
  case Hearts; 
  case Diamonds; 
  case Clubs; 
  case Spades; 
} 

function pick_a_card(Suit $suit) { ... } 

$val = Suit::Diamonds; 

pick_a_card($val);        // OK 
pick_a_card(Suit::Clubs); // OK 
pick_a_card('Spades');    // TypeError: pick_a_card(): Argument #1 ($suit) must be of type Suit, string given 

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

У элементов перечислений есть также свойство «name», которое хранит имя, хоть оно и является артефактом реализации, поэтому его стоит использовать для отладки.

Подкреплённые перечисления

По умолчанию элементы перечислений не имеют скалярного типа, но всё же его можно объявить при создании перечисления (доступно только для int или string):

enum Suit: string { 
  case Hearts = 'H'; 
  case Diamonds = 'D'; 
  case Clubs = 'C'; 
  case Spades = 'S'; 
} 

Такие значения называются подкреплёнными (backed), ведь значение «подкрепляются» более простым скалярным типом.

Также доступно свойство только для чтения «value», хранящее значение.

Подкреплённые перечисления реализуют интерфейс BackedEnum, который определяет два метода:

  • from(int|string): self принимает скалярный тип и возвращает соответствующее значение перечисления (Enum Case), а если такого нет, то будет выброшена ошибка типа ValueError;

  • tryFrom(int|string): ?self аналогичен первому методу, только теперь ошибка не будет выброшена, а просто возвратится null.

Интерфейсы, трейты и методы в перечислениях

Перечисление может вмещать методы (причём даже статические), а также использовать трейты.

Если перечисление реализует какой-то интерфейс, то все элементы перечисления пройдут проверку на тип, реализующий этот интерфейс:


interface Colorful { 
  public function color(): string; 
} 

enum Suit implements Colorful { 
  case Hearts; 
  case Diamonds; 
  case Clubs; 
  case Spades; 

  // Fulfills the interface contract. 

  public function color(): string { 
    return match($this) { 
      Suit::Hearts, Suit::Diamonds => 'Red', 
      Suit::Clubs, Suit::Spades => 'Black', 
    }; 
  } 

  // Not part of an interface; that's fine. 
  public function shape(): string { 
    return "Rectangle"; 
  } 
} 

function paint(Colorful $c) { ... } 

paint(Suit::Clubs);  // Works 

print Suit::Diamonds->shape(); // "rectangle" 

Перечисление значений

Также перечисления реализуют метод cases(), возвращающий список объектов перечисления:

Suit::cases(); 

// [Suit::Hearts, Suit::Diamonds, Suit::Clubs, Suit:Spades] 

Интерсекционные типы

В противовес союзным типам в РНР 8.0, добавлена возможность указания более точных типов, путём их объединения при помощи логического «и». Это достигается благодаря комбинации имён классов или интерфейсов при помощи амперсанда & (пример из php.watch):

function count_and_iterate(Iterator&\Countable $value) { 
    foreach($value as $val) {} 
    count($value); 
} 

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

Нити

Добавлены файберы, предоставляющие низкоуровневый интерфейс для создания «зелёных потоков». Нити позволяют управлять параллельными процессами в пределах одного физического процесса. То есть, де-факто не будет двух участков кода, выполняющихся в один момент времени, но файберы позволяют выполнять действия в момент, пока один участок кода простаивает.

Возвращаемый тип never

Добавлена возможность объявить возвращаемый функцией тип как never, если она всегда вызывает остановку программы. Этого можно достичь, вызывая в этой функции конструкцию exit или die. Пример из PHP.Watch


function redirect(string $url): never { 
    header('Location: ' . $url); 
    exit(); 
} 

Цель этого изменения — указать, что вызов определённой функции гарантированно остановит выполнение программы.

final для констант класса

Добавлена возможность указать константы класса итоговыми, чтобы запретить их переопределение.

class Foo { 
    final public const X = "foo"; 
} 

class Bar extends Foo { 
    public const X = "bar"; 
} 

// Fatal error: Bar::X cannot override final constant Foo::X 

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

Свойства только для чтения

Добавлена поддержка свойств классов только для чтения, значение которых можно устанавливать лишь один раз:

class Test {
    public readonly string $prop;
 
    public function __construct(string $prop) {
        // Заполнение readonly-свойства
        $this->prop = $prop;
    }
}
 
$test = new Test("foobar");
// Так можно
var_dump($test->prop); // string(6) "foobar"
 
// А так нельзя, пускай значение и совпадает
$test->prop = "foobar";
// Error: Cannot modify readonly property Test::$prop

Ключевое слово readonly можно использовать только с типизированным свойством. Инициализация свойства только для чтения может происходить только в классе, содержащем это свойство.

При попытке повторного установления значения или удаления переменной будет выброшено исключение типа Error.

Кроме этого:

  • Добавлены функции:
    • fsync: синхронизация изменений в файле (включая метадату);
    • fdatasync: синхронизация изменений в файле без метадаты;
    • array_is_list: проверяет, является ли массив списком;
    • Функции для поддержки потокового шифрования XChaCha20 в Sodium;
  • Добавлено новое поле full_path в суперглобальном массиве $_FILES, хранящее полный путь к файлам, который был передан браузером;
  • Синтаксис first-class callable;
  • Новый атрибут #[ReturnTypeWillChange];
  • Оператор распаковки массивов теперь поддерживает массивы со строковыми ключами;
  • Добавлена поддержка алгоритмов хеширования xxHash и MurmurHash3;
  • Добавлен новый параметр $options для функций хеширования для определения алгоритмоспецифичных дополнительных опций;
  • Добавлена поддержка префикса 0o/0O для восьмеричных чисел;
  • Новый класс IntlDatePatternGenerator, позволяющий лучше поддерживать локализованные даты;
  • В phar добавлена поддержка алгоритмов подписи OpenSSL-256 и OpenSSL-512;
  • В расширение mysqli добавлена константа MYSQLI_REFRESH_REPLICA аналогичная уже существующей константе MYSQLI_REFRESH_SLAVE, это сделано из-за того, что в MySQL версии 8.0.23 некоторые опции, содержащие слова «slave», были признаны устаревшимы и заменены на аналогичные с «replica». Тем не менее MYSQLI_REFRESH_SLAVE в РНР устаревшей не признана и планов по удалению этой константы нет;
  • GD:
  • Curl:
    • Добавлена поддержка DNS-over-HTTPS;
    • Добавлен класс CURLStringFile, позволяющий осуществлять загрузку файла путём отправки его содержимого.

Изменения

Ограничение использования суперглобального массива $GLOBALS

Суперглобальный массив $GLOBALS, хранящий пары имя_переменной => ссылка_на_переменную больше нельзя передавать по ссылке, приравнивать его к пустому массиву или ссылке на переменную.

// Будет работать:
foreach ($GLOBALS as $var => $value) {
    echo "$var => $value\n";
}
$GLOBALS['x'] = 1;
$GLOBALS['x']++;
isset($GLOBALS['x']);
unset($GLOBALS['x']);
// ...что угодно, используя $GLOBALS['x'].

// Выдаёт ошибку времени компиляции:
$GLOBALS = [];
$GLOBALS += [];
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);
// ...и другие подобные операции чтения-записи над $GLOBALS

// Выдаёт исключения типа Error во время исполнения:
by_ref($GLOBALS);

А также:

  • Функции для работы HTML теперь будут обрабатывать одинарные кавычки ', а невалидные символы теперь будут просто заменены на � (U+FFFD);
  • Теперь класс SplFixedArray реализует интерфейс JsonSerializable, что позволит функции json_encode воспринимать его как массив;
  • Режим интерактивной оболочки (php -a) теперь требует наличия расширения readline;
  • Функции fputcsv и SplFileObject::fputcsv теперь могут принимать новый параметр, оборачивающий конец строки;
  • Функция version_compare() теперь не может принимать частичные значения для операторов;
  • Функция compact() теперь выбрасывает предупреждение, если типы её параметров отличались от массива и строки;
  • Разрешено использование ключевого слова new в параметрах функции по умолчанию, аргументах атрибутов, инициализаторах статических переменных и глобальных констант;
  • Благодаря изменениям в работе OPcache, производительность была повышена на 0.5-8%;
  • В phar алгоритм подписи по умолчанию изменён с SHA1 на SHA256;
  • Расширение MySQLi теперь использует режим выброса исключений при возникновении ошибки;
  • Следующие ресурсы стали объектами:
    • file_info => finfo
    • imap => IMAP\Connection
    • ftp => FTP\Connection
    • Функция imageloadfont теперь возвращает экземпляр класса GdFont
    • Ресурсы LDAP мигрировали в экземпляры классов LDAP\Connection, LDAP\Result и LDAP\ResultEntry
    • Ресурсы PostgreSQL мигрировали в экземпляры классов PgSql\Connection, PgSql\Result и PgSql\Lob
    • pspell => PSpell\Dictionary, pspell config => PSpell\Config

Объявлено устаревшим

>>> Подробности

Нити
Добавлены файберы, предоставляющие низкоуровневый интерфейс для создания «зелёных потоков». Нити позволяют управлять параллельными процессами в пределах одного физического процесса. То есть, де-факто не будет двух участков кода, выполняющихся в один момент времени, но файберы позволяют выполнять действия в момент, пока один участок кода простаивает.

Скачем в терминологии в пределах абзаца, можно определиться в терминологии c нитями, файберами и зелеными потоками?

st4l1k ★★ ()
Последнее исправление: st4l1k (всего исправлений: 1)
Ответ на: комментарий от eternal_sorrow

Если они умные люди, то не выберут

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

Кстати, для PHP есть такие же легковесные фреймворки как flask

Слим, люмен.

fernandos ★★★ ()

Давайте сделаем нормально инициализацию значений у свойств объекта? Типа такого:

final class Service {
    private LoggerInterface $logger = new NullLogger();
}

Нет! Ты что?! Это же PHP!!!111 Давайте делать через одно место, чтить традиции:

final class Service {
    public function __construct(
        private LoggerInterface $logger = new NullLogger(),
    ) {
    }
}

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

Боже как я рад что свичнулся с этого убожества.

istepan ()
Ответ на: комментарий от fernandos

Не все так просто. Есть встроенные в язык объекты и из стандартной библиотеки. Но это придирки к языку, все равно что ругаться на Java что там нельзя функцию отдельно от класса создать.

anonymous ()
Ответ на: комментарий от anonymous

в php это просто лишний символ

Функции без знака доллара. Визуально можно различить $test, test() и $test() - по этому не бесполезно. Как минимум все подсветки синтаксиса легко подкрашивают в разные цвета переменные и функции.

Весь вопрос в том что функционала несет меньше, чем вредит глазам - тут согласен.

nwtour ★★ ()
Ответ на: комментарий от nwtour

Зато стрёмно выглядит получение статического свойства типа $Class::$property.

Пых в целом про написание лишнего. <?php, декларация строгих типов и эти доллары повсюду везде надо пописывать. Для динамического языка он слишком многословен.

anonymous ()
Ответ на: комментарий от fernandos

резон, но не совсем, в php нет compile проверок нормальных перед исполнением, в java есть, поэтому он более статичен. Вопрос в том зачем в динамичном языке хотеть быть как в статичной Java непонятно.

anonymous ()
Ответ на: комментарий от anonymous

За тем, что из языка (пхп3) уже много лет хотят сделать что-то нормальное.

Можно, конечно, было плюнуть, мол, пусть подыхает, но как-то некрасиво да и скучно выходит. Вот разработчики и пытаются сделать что-то нормальное.

fernandos ★★★ ()
Последнее исправление: fernandos (всего исправлений: 1)
Ответ на: комментарий от anonymous

Вопрос не понятен потому что в новых проектах, если они сложные, не хотят динамичности. По той же причине переходят с js на typescript. В php выбрали другой путь - не создавать новый язык с компиляцией в php, а менять сам язык в сторону статической типизации, но сохраняя обратную совместимость, хотя бы частично. Можно пользоваться другими инструментами для статического анализа. Тот же phpstun может заменить анализ во время компиляции как в java или typescript.

anonymous ()
Ответ на: комментарий от no-such-file

У Yii свой подход, у PHP-FIG - свой. Причем судя по тому, что часть PSR deprecated, часть abandoned, а часть типа PSR-7 спровоцировала раскол в самой PHP-FIG - их велосипеды вообще часто плохо ездят. Хоть и бытует мнение, что говняный стандарт лучше чем никакого, но это к сожалению приводит к тому, что говняные решения начинают имплементироваться повсеместно и люди делают это не потому, что считают их правильными, а потому что «стандарт».

vs240 ()
Ответ на: комментарий от vs240

У Yii свой подход, у PHP-FIG - свой

Вроде как в Yii3 обещают исправиться. Так-то в Yii тоже много устарелого шлака, который тянется ещё с первых версий для древнего пыха.

no-such-file ★★★★★ ()
Ответ на: комментарий от fernandos

Ну справедливости ради, сильно бежать торопясь за мажорными релизами тоже смысла немного. Мы вот сейчас как раз тестим 8.1 в хостинге, но пока что большая часть CMS и фреймворков к новой версии просто не готово (что-то просто не работает, что-то не устанавливается дефолтным установщиком, где-то модули под 8.1 еще не собираются, что-то просто срет Warning-ами). И тут бы можно было сказать, что это проблемы говноcms/говнофреймворков, но они все такие и по факту мы находимся в ситуации, когда 8.1-то конечно выкатим, но пользоваться им никто особо не будет. Адопшн даже для 8.0 пока еще довольно кислый, если совсем уж честно. Да - мы у себя новые версии стараемся выкатывать как можно раньше. Однако статистика такова, что еще с ветки 5.x народ только-только стал переползать в товарных количествах и то за это спасибо CMS-кам, которые в админке стали писать, что пора бы на 7-ю ветку.

t500s ★★ ()
Ответ на: комментарий от Psilocybe

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

t500s ★★ ()
Ответ на: комментарий от firkax

Я в курсе что мир полон пых-велосипедистов. Но от этого они правильнее не становятся.

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

Bonio ()