Ліниві сервіси
Дата оновлення перекладу 2024-06-04
Ліниві сервіси
See also
Ще один спосіб впроваджувати ліниві сервіси - через підписника сервісу.
Чому ліниві сервіси?
У деяких випадках, ви можете захотіти впровадити сервіс, який трохи заважкий для інстанціювання,
але не завжди використовується всередині вашого обʼєкта. Наприклад, уявіть, що у вас є
NewsletterManager
і ви впроваджуєте у нього сервіс mailer
. Лише декілька методів у
вашому NewsletterManager
дійсно використовують mailer
, але навіть коли він вам не
потрібний, сервіс mailer
завжди інстанціюється, щоб побудувати ваш NewsletterManager
.
Рішенням цього є ліниві сервіси. З лінивим сервісом насправді впроваджується "проксі"
сервісу mailer
. Він виглядає та поводить себе точно так само, як і mailer
, крім
того, що mailer
насправді не інстанціюється до того як ви почнете якусь взаємодію
з проксі.
Caution
Ліниві сервіси не підтримують класи final, але ви можете використати Проксування інтерфейсу, щоб обійти це обмеження.
У версіях PHP до 8.0, ліниві сервіси не підтримують параметри зі значеннями
за замовчуванням для вбудованих PHP-класів (наприклад, PDO
).
Конфігурація
Ви можете відмітити сервіс як lazy
, змінивши його визначення:
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"
Ви також можете сконфігурувати лінивість вашого сервісу завдяки атрибуту Autoconfigure. Наприклад, щоб визначити ваш сервіс як лінивий, використайте наступне:
1 2 3 4 5 6 7 8 9 10
namespace App\Twig;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Twig\Extension\ExtensionInterface;
#[Autoconfigure(lazy: true)]
class AppExtension implements ExtensionInterface
{
// ...
}
Ви також можете сконфігурувати лінивість, коли ваш сервіс впроваджується за допомогою атрибута Autowire:
1 2 3 4 5 6 7 8 9 10 11 12 13
namespace App\Service;
use App\Twig\AppExtension;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class MessageGenerator
{
public function __construct(
#[Autowire(service: 'app.twig.app_extension', lazy: true)] ExtensionInterface $extension
) {
// ...
}
}
Цей атрибут також дозволяє вам визначити інтерфейси для проксі при використанні лінивості, і підтримує ліниве автомонтування типів об'єднань:
1 2 3 4 5
public function __construct(
#[Autowire(service: 'foo', lazy: FooInterface::class)]
FooInterface|BarInterface $foo,
) {
}
Іншою можливістю є використання атрибута Lazy:
1 2 3 4 5 6 7 8 9 10
namespace App\Twig;
use Symfony\Component\DependencyInjection\Attribute\Lazy;
use Twig\Extension\ExtensionInterface;
#[Lazy]
class AppExtension implements ExtensionInterface
{
// ...
}
Цей атрибут можна застосовувати як до класу, так і до параметрів, які мають бути ліниво завантажені. Він визначає необов'язковий параметр, який використовується для визначення інтерфейсів для типів проксі та перетину:
1 2 3 4 5
public function __construct(
#[Lazy(FooInterface::class)]
FooInterface|BarInterface $foo,
) {
}
7.1
Атрибут #[Lazy]
було представлено в Symfony 7.1.
Проксування інтерфейсу
За лаштунками, проксі, згенеровані для лінивого завантаження сервісів, наслідують з класу, використовуваного сервісом. Однак, іноди це взагалі неможливо (наприклад, через те, що клас - final і не може бути розширений) або незручно.
Щоб обійти це обмеження, ви можете сконфігурувати проксі, щоб він реалізовував тільки конкретні інтерфейси.
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' }
Як і в розділі Конфігурація , ви можете використати
атрибут Autoconfigure, щоб
сконфігурувати інтерфейс для проксування, передавши його FQCN як значення параметра lazy
:
1 2 3 4 5 6 7 8 9 10
namespace App\Twig;
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
use Twig\Extension\ExtensionInterface;
#[Autoconfigure(lazy: ExtensionInterface::class)]
class AppExtension implements ExtensionInterface
{
// ...
}
Віртуальний проксі, впроваджений в інші сервіси, реалізовуватиме лише вказані інтерфейси і не розширюватиме оригінальний клас сервісу, що дозволить ліниве завантаження сервісів з використанням класів final. Ви можете сконфігурувати проксі так, щоб він реалізовував декілька інтерфейсів, додавши нові теги "proxy".
Tip
Ця функція може також діяти як охороень: враховуючи, що проксі не розширює оригінальний клас, лише методи, визначені інтерфейсом, можуть бути викликані; що запобігає виклику реалізації конкретних методів. Це також запобоігає впровадженню залежності взагалі, якщо ви додали підказку конкретної реалізації, замість інтерфейсу.