class Status
{
const DRAFT = 'draft';
const PUBLISHED = 'published';
const ARCHIVED = 'archived';
}
function acceptStatus(string $status) {...}enum Status
{
case Draft;
case Published;
case Archived;
}
function acceptStatus(Status $status) {...}class BlogData
{
private Status $status;
public function __construct(Status $status)
{
$this->status = $status;
}
public function getStatus(): Status
{
return $this->status;
}
}class BlogData
{
public readonly Status $status;
public function __construct(Status $status)
{
$this->status = $status;
}
}Readonly-свойства нельзя изменить после инициализации (т.е. когда им было присвоено значение).
Они будут крайне полезны при реализации объектов типа VO и DTO.
$foo = [$this, 'foo'];
$fn = Closure::fromCallable('strlen');$foo = $this->foo(...);
$fn = strlen(...);С помощью нового синтаксиса любая функция может выступать в качестве объекта первого класса. Тем самым она будет рассматриваться как обычное значение, которое можно, например, сохранить в переменную.
class Service
{
private Logger $logger;
public function __construct(
?Logger $logger = null,
) {
$this->logger = $logger ?? new NullLogger();
}
}class Service
{
private Logger $logger;
public function __construct(
Logger $logger = new NullLogger(),
) {
$this->logger = $logger;
}
}Объекты теперь можно использовать в качестве значений параметров по умолчанию, статических переменных и глобальных констант, а также в аргументах атрибутов.
Таким образом появилась возможность использования вложенных атрибутов.
class User
{
/**
* @Assert\All({
* @Assert\NotNull,
* @Assert\Length(min=5)
* })
*/
public string $name = '';
}class User
{
#[\Assert\All(
new \Assert\NotNull,
new \Assert\Length(min: 5))
]
public string $name = '';
}function count_and_iterate(Iterator $value) {
if (!($value instanceof Countable)) {
throw new TypeError('value must be Countable');
}
foreach ($value as $val) {
echo $val;
}
count($value);
}function count_and_iterate(Iterator&Countable $value) {
foreach ($value as $val) {
echo $val;
}
count($value);
}Теперь в объявлении типов параметров можно указать, что значение должно относиться к нескольким типам одновременно.
В данный момент пересечения типов нельзя использовать вместе с объединёнными типами, например, A&B|C.
function redirect(string $uri) {
header('Location: ' . $uri);
exit();
}
function redirectToLoginPage() {
redirect('/login');
echo 'Hello'; // <- dead code
}function redirect(string $uri): never {
header('Location: ' . $uri);
exit();
}
function redirectToLoginPage(): never {
redirect('/login');
echo 'Hello'; // <- dead code detected by static analysis
}Функция или метод, объявленные с типом never, указывают на то, что они не вернут значение и либо выбросят исключение, либо завершат выполнение скрипта с помощью вызова функции die(), exit(), trigger_error() или чем-то подобным.
class Foo
{
public const XX = "foo";
}
class Bar extends Foo
{
public const XX = "bar"; // No error
}class Foo
{
final public const XX = "foo";
}
class Bar extends Foo
{
public const XX = "bar"; // Fatal error
}Теперь константы класса можно объявить как окончательные (final), чтобы их нельзя было переопределить в дочерних классах.
016 === 16; // false because `016` is octal for `14` and it's confusing
016 === 14; // true 0o16 === 16; // false — not confusing with explicit notation
0o16 === 14; // true Теперь можно записывать восьмеричные числа с явным префиксом 0o.
$httpClient->request('https://example.com/')
->then(function (Response $response) {
return $response->getBody()->buffer();
})
->then(function (string $responseBody) {
print json_decode($responseBody)['code'];
});$response = $httpClient->request('https://example.com/');
print json_decode($response->getBody()->buffer())['code'];Файберы — это примитивы для реализации облегчённой невытесняющей конкурентности. Они являются средством создания блоков кода, которые можно приостанавливать и возобновлять, как генераторы, но из любой точки стека. Файберы сами по себе не предоставляют возможностей асинхронного выполнения задач, всё равно должен быть цикл обработки событий. Однако они позволяют блокирующим и неблокирующим реализациям использовать один и тот же API.
Файберы позволяют избавиться от шаблонного кода, который ранее использовался с помощью Promise::then() или корутин на основе генератора. Библиотеки обычно создают дополнительные абстракции вокруг файберов, поэтому нет необходимости взаимодействовать с ними напрямую.
$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = array_merge(['a' => 0], $arrayA, $arrayB);
// ['a' => 1, 'b' => 2]$arrayA = ['a' => 1];
$arrayB = ['b' => 2];
$result = ['a' => 0, ...$arrayA, ...$arrayB];
// ['a' => 1, 'b' => 2]PHP раньше поддерживал распаковку массивов с помощью оператора ..., но только если массивы были с целочисленными ключами. Теперь можно также распаковывать массивы со строковыми ключами.
timelib и ext/date.serialize()/unserialize().get_declared_classes(), explode(),
strtr(), strnatcmp(), dechex()).#[ReturnTypeWillChange].fsync и fdatasync.array_is_list.NULL параметрам встроенных функций, не допускающим значение NULL, объявлена устаревшей.Serializable объявлен устаревшим.$GLOBALS.file_info заменены на объекты finfo.imap заменены на объекты IMAP\Connection.Connection заменены на объекты FTP\Connection.Font identifiers заменены на объекты GdFont.LDAP\Connection, LDAP\Result и LDAP\ResultEntry.PgSql\Connection, PgSql\Result и PgSql\Lob.pspell, pspell config заменены на объекты PSpell\Dictionary, PSpell\Config.