Как заставить службу работать на kernel.terminate, как работает SwiftMailer?
Мне было интересно, как написать сервис, который выполняется только на kernel.terminate
.
Например, когда я звоню:
$message = (new \Swift_Message('A new book has been added'))
->setFrom('system@example.com')
->setTo('contact@les-tilleuls.coop')
->setBody(sprintf('The book #%d has been added.', $book->getId()));
$this->mailer->send($message);
Я знаю, что ядро отправит ответ клиенту и только ПОСЛЕ этого отправит электронное письмо.
У меня много услуг, к которым можно обратиться kernel.terminate
но я не знаю, как их написать, чтобы меня вызывали только на этом мероприятии.
А пока пишу код в подписчике:
public static function getSubscribedEvents()
{
return [
KernelEvents::TERMINATE => [['doHeavyStuff', EventPriorities::POST_RESPOND]]
];
}
Но работа таким образом означает, что я должен иметь дело только с запросом и ответом, и я не хочу зависеть от ответа.
if($method===Request::METHOD_PUT && isset($response['@type']) && $response['@type']==='Partner'){
$this->notificationManager->sendNotificationAboutCart($response['partner']['id'],
$response['partner']['name']);
}
Не знаю, понятно ли, но было бы здорово называть notificationManager
везде, где я хочу в своем коде, и этот менеджер работает только с kernel.terminate
.
1 ответ
Простая наивная реализация.
Ваш сервис send()
фактически ничего не отправляет, а просто добавляет еще одно сообщение, которое нужно отправить, когда придет время.
class ShinyService {
private $messages = [];
public function send($message) {
$this->messages[] = $message;
}
public function processMessages()
{
foreach ($this->messages as $message) {
// do the actual work, e.g. sending the message
}
}
}
Затем подписчик, который зависит от этой службы, поэтому он вводится в экземпляр подписчика:
class ShinySubscriber implements EventSubscriberInterface
{
private $service;
public function __construct(ShinyService $service) {
$this->service = $service;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::TERMINATE => [
['processMessages', 10]
],
];
}
public function processMessages(TerminateEvent $event)
{
$this->service->processMessages();
}
}
Таким образом вы можете ввести ShinyService
куда угодно, звони ShinyService::send()
в любой момент, и сообщения будут отправлены только на KernelEvents::TERMINATE
.
Помните, что это событие запускается только после отправки ответа, если вы используете PHP-FPM. Из документов:
Внутри HttpKernel использует функцию PHP fastcgi_finish_request. Это означает, что на данный момент только API сервера PHP FPM может отправлять ответ клиенту, в то время как процесс PHP сервера все еще выполняет некоторые задачи. Со всеми другими API-интерфейсами сервера прослушиватели kernel.terminate все еще выполняются, но ответ не отправляется клиенту, пока все они не будут завершены.