Ленивые сервисы

Ленивые сервисы

Ещё один способом лениво внедрять сервисы - через настройщик сервисов.

Почему ленивые сервисы?

В некоторых случаях, вы можете захотеть внедрить сервис, который немного тяжёлый для инстанциации, но не всегда используется внутри вашего объекта. Например, представьте, что у вас есть NewsletterManager и вы внедряете в него сервис mailer. Только несколько методов в вашем NewsletterManager действительно используют mailer, но даже когда он вам не нужн, сервис mailer всегда инстанциируется, чтобы построить ваш NewsletterManager.

Решением этого являются ленивые сервисы. С ленивым сервисом на самом деле внедряется "прокси" сервиса mailer. Он выглядит и ведёт себя точно так же, как mailer, кроме того, что mailer на самом деле не инстанциируется до того, как вы начнёте какое-либо взаимодействие с прокси.

Установка

Чтобы использовать инстанциацию ленивого сервиса, вам всегда в первую очередь понадобится установить пакет ocramius/proxy-manager:

1
$ composer require ocramius/proxy-manager

Note

Если вы не используете комплексный фреймворк, вы также должны установить мост ProxyManager

1
$ composer require symfony/proxy-manager-bridge

Конфигурация

Вы можете отметить сервис как lazy (ленивый), изменив его определение:

  • YAML
    1
    2
    3
    services:
       AppBundle\Twig\AppExtension:
         lazy:  true
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    <?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
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\Twig\AppExtension" lazy="true" />
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    use AppBundle\Twig\AppExtension;
    
    $container->register(AppExtension::class)
        ->setLazy(true);
    

Как только вы внедрите сервис в другой сервис, должен внедриться виртуальный прокси с той же подписью класса, который будет представлять сервис. То же самое происходит при вызове Container::get() напрямую.

Сам класс будет инстанциирован как только вы попробуете взаимодействовать с сервисом (например, вызвать один из его методов).

Чтобы проверить, работает ли ваш прокси, вы можете просто проверить интерфейс полученного объекта:

1
2
dump(class_implements($service));
// вывод должен включать в себя "ProxyManager\Proxy\LazyLoadingInterface"

Note

Если вы не установите мост ProxyManager и ocramius/proxy-manager, контейнер будет просто пропускать метку lazy и инстанциировать сервис, как обычно.

Дополнительные источники

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

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.