Search code examples
consolesymfonybackground-processshell-exec

Symfony - run console command on kernel.terminate


I have configured swiftmailer to spool emails using file type. here is my swiftmailer config

swiftmailer:
    transport: "%mailer_transport%"
    host:      "%mailer_host%"
    username:  "%mailer_user%"
    password:  "%mailer_password%"
    spool:
        type: file
        path: "%kernel.root_dir%/../var/spool"

When I send any emails it perfectly spools. I run following command to dispatch emails thereafter.

bin/console swiftmailer:spool:send --env=dev

According to Symfony documentation

the console command should be triggered by a cron job or scheduled task and run at a regular interval.

My problem is, I cannot use crontab because cron can be configured with a minimum of 1 minute interval which I cannot afford. I want to make use of the background process with immediate execution after the response is sent back to browser, hence minimizing execution of spools to bare minimum.

I attempted to solve this problem by creating an event listener class and listening to kernel.terminate, and execute the command using shell_exec or exec function, here is the code for reference.

app.kernel.terminate.listener:
        arguments: ["@kernel"]
        class: AppBundle\EventListener\KernelTerminateListener
        tags:
            - { name: kernel.event_listener, event: kernel.terminate }

Here is my EventListener class

<?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Cocur\BackgroundProcess\BackgroundProcess;

class KernelTerminateListener
{
    protected $kernel;

    protected $console;

    public function __construct($kernel)
    {
        $this->kernel = $kernel;
        $this->console = $this->kernel->getRootDir().'/../bin/console ';
    }

    public function onKernelTerminate(PostResponseEvent $event)
    {
        $command = $this->console.'swiftmailer:spool:send --env='.$this->kernel->getEnvironment();
        shell_exec($command);
    }
}

What I am trying in here is to run bin/console swiftmailer:spool:send --env=dev on kernel.terminate event, unfortunately this does not work, any hint on how to approach this problem is appreciated.

Thank you.


Solution

  • Please use the memory spool type of swift mailer, it does exactly what you want

    When you use spooling to store the emails to memory, they will get sent right before the kernel terminates. This means the email only gets sent if the whole request got executed without any unhandled exception or any errors. To configure swiftmailer with the memory option, use the following configuration: