Ліниві сервіси

Дата оновлення перекладу 2022-12-23

Ліниві сервіси

See also

Ще один спосіб впроваджувати ліниві сервіси - через підписника сервісу.

Чому ліниві сервіси?

У деяких випадках, ви можете захотіти впровадити сервіс, який трохи заважкий для інстанціювання, але не завжди використовується всередині вашого обʼєкта. Наприклад, уявіть, що у вас є NewsletterManager і ви впроваджуєте у нього сервіс mailer. Лише декілька методів у вашому NewsletterManager дійсно використовують mailer, але навіть коли він вам не потрібний, сервіс mailer завжди інстанціюється, щоб побудувати ваш NewsletterManager.

Рішенням цього є ліниві сервіси. З лінивим сервісом насправді впроваджується "проксі" сервісу mailer. Він виглядає та поводить себе точно так само, як і mailer, крім того, що mailer насправді не інстанціюється до того як ви почнете якусь взаємодію з проксі.

Caution

Ліниві сервіси не підтримують класи final, але ви можете використати Проксування інтерфейсу, щоб обійти це обмеження.

У версіях PHP до 8.0, ліниві сервіси не підтримують параметри зі значеннями за замовчуванням для вбудованих PHP-класів (наприклад, PDO).

6.2

Починаючи з Symfony 6.2, вам не потрібно встановлювати ніякий пакет (наприклад, symfony/proxy-manager-bridge), щоб використовувати інстанціювання лінивого сервісу.

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

Ви можете відмітити сервіс як lazy, змінивши його визначення:

  • YAML
  • XML
  • PHP
1
2
3
4
# config/services.yaml
services:
    App\Twig\AppExtension:
        lazy: true

Як тільки ви впровадите сервіс в інший сервіс має бути впроваджено лінивий обʼєкт-привид з таким же підписом класу, що представляє сервіс. Лінивий обʼєкт-привид - це обʼєкт, який створюється порожнім і який може ініціалізувати сам себе, коли до нього отримують доступ вперше. Теж саме відбувається, якщо викликати Container::get() напряму.

Щоб перевірити, чи працює ваш лінивий сервіс, ви можете перевірити інтерфейс отриманого обʼєкта:

1
2
dump(class_implements($service));
// виведення повинно включати в себе "Symfony\Component\VarExporter\LazyGhostObjectInterface"

Проксування інтерфейсу

За лаштунками, проксі, згенеровані для лінивого завантаження сервісів, наслідують з класу, використовуваного сервісом. Однак, іноди це взагалі неможливо (наприклад, через те, що клас - final і не може бути розширений) або незручно.

Щоб обійти це обмеження, ви можете сконфігурувати проксі, щоб він реалізовував тільки конкретні інтерфейси.

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# config/services.yaml
services:
    App\Twig\AppExtension:
        lazy: 'Twig\Extension\ExtensionInterface'
        # or a complete definition:
        lazy: true
        tags:
            - { name: 'proxy', interface: 'Twig\Extension\ExtensionInterface' }

Віртуальний проксі, впроваджений в інші сервіси, реалізовуватиме лише вказані інтерфейси і не розширюватиме оригінальний клас сервісу, що дозволить ліниве завантаження сервісів з використанням класів final. Ви можете сконфігурувати проксі так, щоб він реалізовував декілька інтерфейсів, додавши нові теги "proxy".

Tip

Ця функція може також діяти як охороень: враховуючи, що проксі не розширює оригінальний клас, лише методи, визначені інтерфейсом, можуть бути викликані; що запобігає виклику реалізації конкретних методів. Це також запобоігає впровадженню залежності взагалі, якщо ви додали підказку конкретної реалізації, замість інтерфейсу.