Валентин Удальцов
Ведущий разработчик в
@vudaltsov, @phpyh

function 🥁(🙁): 😁 { }

str_contains()

str_contains(string $haystack, string $needle): bool
assertSame(
    $needle === '' || strpos($haystack, $needle) !== false,
    str_contains($haystack, $needle),
);

📝 RFC by Philipp Tanlak

💊 Symfony Polyfill

str_starts_with(), str_ends_with()

str_starts_with(string $haystack, string $needle): bool
str_ends_with  (string $haystack, string $needle): bool
assertSame(
    strncmp($haystack, $needle, strlen($needle)) === 0,
    str_starts_with($haystack, $needle),
);

📝 RFC by Will Hudgins

💊 Symfony Polyfill

get_debug_type()

get_debug_type(mixed $value ): string
$value gettype($value) get_debug_type($value)
null NULL null
true boolean bool
false boolean bool
123 integer int
3.141 double float
'Foo' string string
[1, 2] array array

📝 RFC by Mark Randall

get_debug_type()

$value gettype($value) get_debug_type($value)
new Foo\Bar() object Foo\Bar
function() {} object Closure
new class {} object class@anonymous
new class extends Foo {} object Foo@anonymous
new class implements Foo {} object Foo@anonymous
Ресурс resource resource (TYPE)
Закрытый ресурс resource (closed) resource (closed)

💊 Symfony Polyfill

preg_last_error_msg()

function preg_last_error_msg(): string
{
    switch (preg_last_error()) {
        case PREG_INTERNAL_ERROR:
            return 'Internal error';
        case PREG_BAD_UTF8_ERROR:
            return 'Malformed UTF-8 characters...';

        // ...

        default:
            return 'Unknown error';
    }
}

📝 PR by Nico Oelgart

💊 Symfony Polyfill

fdiv()

Деление в соответствии со стандартом IEEE-754.

fdiv(float $x, float $y): float
assertSame(fdiv( 0.5, 0),  INF);
assertSame(fdiv(-0.5, 0), -INF);
assertSame(fdiv(   0, 0),  NAN);

📝 PR by Nikita Popov

💊 Symfony Polyfill

get_resource_id()

function get_resource_id($resource): int
{
    if (!is_resource($resource)) {
        throw new TypeError();
    }

    return (int) $resource;
}

📝 PR by Nikita Popov

💊 Symfony Polyfill

DateTime::createFromInterface()

public static DateTime::createFromInterface(
    DateTimeInterface $dateTime
): DateTime
public static DateTimeImmutable::createFromInterface(
    DateTimeInterface $dateTime
): DateTimeImmutable

📝 PR by Mike Simonson

final class 🐛 { public function transform(): 🦋 {} }

Stringable

interface Stringable
{
    public function __toString(): string;
}

Автоматически добавляется к классам, которые имплементируют __toString().

function printString(string|Stringable): void
{
}

📝 RFC by Nicolas Grekas

💊 Symfony Polyfill

WeakMap

$metadata = new WeakMap();

$object = new stdClass(); // ref_count=1
$metadata[$object] = 'some metadata'; // ref_count=1, not 2 🔴

var_dump(count($metadata)); // int(1)

unset($object); // ref_count=0

var_dump(count($metadata)); // int(0)

📝 RFC by Nikita Popov

PhpToken

PHP < 8

foreach (token_get_all($code) as $token) {
    if (!is_string($token)) {
        [$pos, $text, $line] = $token;
    }
}

PHP >= 8

foreach (PhpToken::tokenize($code) as $token) {
    $token->id;
    $token->line;
    $token->pos;
    $token->is([T_CLASS, T_CONST]);
}

📝 RFC by Nikita Popov

💊 PHPWatch Polyfill

Миграция ресурсов в объекты

- /**
-  * @return resource|false
-  */
- function curl_init() {}

+ function curl_init(): CurlHandle|false {}

Аналогично, GDImage, Socket, OpenSSL.

📝 PRs #5402, #4714, #5900, #5860

mixed = 🍏 | 🍋 | 🍓 | 🍌

mixed

null|bool|int|float|string|array|object|callable|resource

Объединение с любым типом, например, null|mixed, бросит фатальную ошибку.

final class Example
{
    public mixed $property;

    public function method(mixed $argument): mixed {}
}

📝 RFC by Máté Kocsis and Michael Moravec

Объединённые типы

iterable = array|Traversable
?string = null|string
final class Number
{
    private int|float $number;

    public function __construct(int|float $number)
    {
        $this->number = $number;
    }

    public function toPrimitive(): int|float
    {
        return $this->number;
    }
}

Объединённые типы. Рефлексия

$returnType = (new ReflectionClass(Number::class))
    ->getMethod('toPrimitive')
    ->getReturnType()
;

if ($returnType instanceof ReflectionUnionType) {
    foreach ($returnType->getTypes() as $type) {
        // ...
    }
}

📝 RFC by Nikita Popov

static

abstract class ValueObject
{
    final public static function fromString(string $value): static
    {
        return new static($value);
    }

    final public function withValue(string $value): static
    {
        $clone = clone $this;
        $clone->value = $value;

        return $clone;
    }
}

📝 RFC by Nikita Popov

match (👨‍🦰) { 👧 => 👫, 🧑 => 👬, };

$object::class

$object::class === get_class($object); // true

📝 RFC by Nikita Popov

throw как выражение

static fn() => throw new Exception('have a nice day!');
$foo = isset($bar['baz'])
    ? $bar['baz']
    : throw new Exception('$bar[baz] is not set');

$foo = $bar ?: throw new Exception('$bar is falsy');

$foo = $bar ?? throw new Exception('$bar is not set');

$foo ??= throw new Exception('$foo is not set');

📝 RFC by Ilija Tovilo

Выражение match

PHP < 8

switch ($request->getMethod())
{
    case 'POST':
        $status = $this->handlePost($request);
        break;

    case 'GET':
    case 'HEAD':
        $status = $this->handleGet($request);
        break;

    default:
        throw new Exception('Unsupported method');
}

Выражение match

PHP >= 8

$status = match($request->getMethod()) {
    'POST'        => $this->handlePost($request),
    'GET', 'HEAD' => $this->handleGet($request),
    default       => throw new Exception('Unsupported method'),
};

📝 RFC by Ilija Tovilo

Висячая запятая в параметрах и use

final class AnswerQuestion
{
    public function __construct(
        string $respondentId,
        string $questionId,
        string $answerId,
    ) {}
}
static function () use(
    $respondentId,
    $questionId,
    $answerId,
): void {}

📝 Parameters RFC by Nikita Popov
📝 Use RFC by Tyson Andre

Свойства в конструкторе

PHP < 8

final class Point
{
    public float $x;
    public float $y;

    public function __construct(float $x = 0.0, float $y = 0.0)
    {
        assert($x >= 0.0);
        assert($y >= 0.0);

        $this->x = $x;
        $this->y = $y;
    }
}

Свойства в конструкторе

PHP >= 8

final class Point
{
    public function __construct(
        public float $x = 0.0,
        public float $y = 0.0,
    ) {
        assert($x >= 0.0);
        assert($y >= 0.0);
    }
}

📝 RFC by Nikita Popov

#[Атрибуты]

#[
  ORM\Entity,
  ORM\Table("recipient")
]
final class Recipient
{
    /** @psalm-readonly */
    #[ORM\Id, ORM\Column("uuid")]
    private string $id;

    #[ORM\Column("string", ORM\Column::UNIQUE)]
    private string $email;
}

📝 Implementation RFC by Benjamin Eberlei
📝 Syntax RFC by Derick Rethans, Benjamin Eberlei

#[Атрибуты]

final class ApproveApplications
{
    /**
     * @Assert\All({
     *     @Assert\NotBlank(),
     *     @Assert\Uuid(),
     * })
     */
    public array $applicationsIds = [];
}

💬 Symfony issue #38503

Именованные аргументы

- htmlspecialchars($string, default, default, false);

+ htmlspecialchars($string, double_encode: false);
array_fill(...[
    'start_index' => 0,
    'count' => 100,
    'value' => 50,
]);
#[@ORM\OneToMany(
    targetEntity: Criterion::class,
    indexBy: "criterionId",
)]

📝 RFC by Nikita Popov

Nullsafe оператор ?->

function identity(): ?Identity
{
    $token = tokenStorage()->token();

    if ($token === null) {
        return null;
    }

    return $token->identity();
}
function identity(): ?Identity
{
    return tokenStorage()->token()?->user();
}

📝 RFC by Ilija Tovilo

try { yield 😍️(); } catch (🔞) { return; }

ValueError

class ValueError extends \Error
{
}

PHP < 8.0

strpos('s', 'small', 16);

// Warning: strpos(): Offset not contained in string in ... on line ...

PHP >= 8.0

strpos('s', 'small', 16);

// Uncaught ValueError: Offset not contained in string in ...:...

📝 RFC by Nikita Popov

💊 Symfony Polyfill

catch без переменной

try {
    return $container->get('logger');
}
- catch (NotFoundExceptionInterface $exception) {
+ catch (NotFoundExceptionInterface) {
    return new NullLogger();
}

📝 RFC by Max Semenik

🚫 🛑 ❌ ⛔️

  • Fatal error во всех случаях нарушения LSP.
  • Fatal error при статическом вызове динамического метода.
  • Оператор подавления @ больше не влияет на вывод фатальных ошибок.
  • PDO по умолчанию бросает исключения
    PDO::ATTR_ERRMODE = PDO::ERRMODE_EXCEPTION.

🤓 $ vim php.ini

ESQ + :q!

error_reporting=E_ALL

- error_reporting=E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED

+ error_reporting=E_ALL

📝 PR by Nikita Popov

ext-json

{
    "require": {
        "php": "^8.0",
-       "ext-json": "*"
    }
}

📝 RFC by Tyson Andre

assert.exception = 1 по умолчанию

assert(true === false);

// Fatal error: Uncaught AssertionError ...

📝 PR by Levi Morrison

🍰 JIT

+ opcache.jit_buffer_size=32M

📝 RFC by Dmitry Stogov

Динамический порт встроенного сервера

php -S localhost:0

[Wed Nov 25 20:35:59 2020] PHP 8.0.0 Development Server
(http://localhost:11584) started
var_dump($_SERVER['SERVER_PORT']);

📝 Commit by Sara Golemon

Источники

Пых @phpyh

PHP Digest @phpdigest

  PHP Point