Ошибки при работе с Typed properties

В PHP 7.4, вышедшем без малого год назад, появились typed properties. Вообще мы и раньше могли контролировать типы свойств классов через геттеры и сеттеры, но контроль был не полный. Теперь если свойство объявлено как int, то теперь в нем строка уже никак не окажется.

Но вместе с новыми возможностями приходят новые проблемы. Появилось новая ошибка Typed property must not be accessed before initialization , которая возникает при попытке обратиться к типизированному свойству, в котором не задано значение. Ранее такое обращение вернуло бы null. Сейчас поведение изменилось. Даже nullable свойства кинут ошибку, если их попытаются считать до того как заполнят. Смотрим новое поведение:

<?php
declare(strict_types=1);

class MyClass
{
    protected int $first;

    protected ?int $second;

    protected ?int $third = null;

    function testProperties()
    {
        // Causes "Typed property MyClass::$first must not be accessed before initialization"
        // $this->first;

        // Causes "Typed property MyClass::$second must not be accessed before initialization"
        // $this->second;

        // Ok
        $this->third;
    }
}

Теперь типизируя свойства надо быть уверенными в том, что они задаются до использования. Больше всего проблем это сулит при работе с сериализуемыми DTO. Например попробуем сериализовать объект с типизированными свойствами:

<?php
declare(strict_types=1);

use JMS\Serializer\Annotation as Serializer;

class Test
{
    /**
     * @Serializer\SerializedName("code")
     * @Serializer\Type("string")
     */
    private string $code;

    /**
     * @Serializer\SerializedName("message")
     * @Serializer\Type("string")
     */
    private string $message;
    
    //  getters + setters
}
// Добавляем в MyClass


    function testSerialize()
    {
        /** @var ArrayTransformerInterface $arrayTransformer */
        $test = new Test();

        //  Causes "Typed property Test::$code must not be accessed before initialization"
        $arrayTransformer->toArray($test);
    }

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

Дополнительные материалы – отличный разбор типизированных свойств: https://stitcher.io/blog/typed-properties-in-php-74