Новое в Symfony 4.3: Настройка служб с неизменяемыми установщиками

Обычная потребность в некоторых приложениях Symfony – использовать неизменяемые сервисы, в то же время используя черты для составления их дополнительных функций. Хотя сервисный контейнер Symfony поддерживает внедрение сеттера, у него есть некоторые недостатки (например, сеттеры могут вызываться не только во время создания, поэтому вы не можете быть уверены, что зависимость не будет заменена в течение времени жизни объекта).

Образцом для решения этой проблемы являются “методы вялости“. Эти методы обычно используют слово with в качестве префикса своих имен (например, withPropertyName()), и они возвращают копию экземпляра неизменяемого класса только с одним измененным свойством:

class MyService
{
    use LoggerAwareTrait;

    // ...
}

trait LoggerAwareTrait
{
    private $logger;

    public function withLogger(LoggerInterface $logger)
    {
        $new = clone $this;
        $new->logger = $logger;

        return $new;
    }
}

$service = new MyService();
$service = $service->withLogger($logger);

Это решает наиболее существенные проблемы инъекций сеттера. Вот почему в Symfony 4.3 добавили поддержку «вихревых методов» в сервисный контейнер.

При использовании YAML для настройки служб передайте true в качестве третьего необязательного параметра вызова метода:

# config/services.yaml
services:
    MyService:
        # ...
        calls:
            # the TRUE argument turns this into a wither method
            - ['withLogger', ['@logger'], true]

При использовании XML для настройки служб установите для параметра return-clone значение true в вызове метода:

<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="MyService">
            <!-- ... -->
            <call method="withLogger" returns-clone="true">
                <argument type="service" id="logger"/>
            </call>
        </service>
    </services>
</container>

При использовании автосопровождения служб для настройки служб добавьте @return статическая аннотация PHPdoc для вихревых методов:

class MyService
{
    // ...

    /**
     * @required
     * @return static
     */
    public function withLogger(LoggerInterface $logger)
    {
        // ...
    }
}