У класичному PHP-FPM застосунок народжується й помирає з кожним запитом, тож singleton() фактично означає «один на запит». Під Octane чи у воркері черги процес живе годинами - і різниця стає критичною.
singleton()- один екземпляр на весь процес. Під Octane він переживає запит і обслуговує наступних користувачів.scoped()- один екземпляр на запит чи завдання. Контейнер скидає його на початку кожного.
$this->app->singleton(ExchangeRates::class); // незмінні дані - безпечно
$this->app->scoped(CurrentCart::class); // стан користувача - лише scoped
Класична помилка:
$this->app->singleton(CartService::class, fn ($app) => new CartService($app['request']->user()));
Під Octane перший користувач «приклеїться» до сервісу, і наступні побачать його кошик.
Правила:
- синглтон не тримає стану запиту: користувача,
Request, локаль, налаштування з сесії; - якщо сервісу потрібен запит -
scoped()або передавати запит у метод, а не в конструктор; - статичні властивості й кеш у пам'яті класу так само переживають запит.
Те саме стосується воркерів черги: завдання N бачить синглтони, створені під час завдання N-1.