Table of Contents
Allows you to create daemonized commands with the React event-loop component.
Via composer :
composer require m6web/daemon-bundle
Note:
- If you are using a symfony version
>= 4.3
use the lastest version - For symfony versions between
2.3
and3.0
, you can usem6web/daemon-bundle:^1.4
- For PHP versions
>=5.5.9
and<7.0
support, you can usem6web/daemon-bundle:^3.0
For more information about installation of plugin refers the documentation of symfony for your version.
You can optionally define events which are triggered each X iterations:
m6_web_daemon:
iterations_events:
-
count: 10
name: Path\From\Your\Project\Event\EachTenEvent
-
count: 5
name: Path\From\Your\Project\Event\EachFiveEvent
Your event need to extends the AbstractDaemonEvent like following:
<?php
namespace Path\From\Your\Project\Event;
use M6Web\Bundle\DaemonBundle\Event\AbstractDaemonEvent;
class EachFiveEvent extends AbstractDaemonEvent
{
}
This bundle use the PSR-14 implementation for event dispatcher so you need to register the symfony event dispatcher in your config/services.yaml
like this:
# config/services.yaml
services:
# ... others declarations
Psr\EventDispatcher\EventDispatcherInterface: "@event_dispatcher"
This command uses the event-loop component which ReactPHP is used to run loops and other asynchronous tasks.
<?php
namespace App\Command;
use M6Web\Bundle\DaemonBundle\Command\DaemonCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DaemonizedCommand extends DaemonCommand
{
protected function configure()
{
$this
->setName('daemonized:command')
->setDescription('My daemonized command');
}
protected function setup(InputInterface $input, OutputInterface $output): void
{
// Set up your daemon here
// Add your own optional callback : called every 10 iterations
$this->addIterationsIntervalCallback(10, [$this, 'executeEveryTenLoops']);
// You can add your own Periodic Timer,
// Here this timer will be called every 0.5s
$daemon = $this;
$this->loop->addPeriodicTimer(0.5, function ($timer) use ($daemon) {
// It's the last loop, cancel the timer.
if ($daemon->isLastLoop()) {
$daemon->loop->cancelTimer($timer);
}
});
}
/**
* Execute is called at every loop
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("Iteration");
// This method helps to give back the CPU to the react-loop.
// So you can wait between two iterations if your workers has nothing to do.
$this->setNextIterationSleepingTime(1000000); // Every second
}
/**
* executeEveryTenLoops is called every 10 loops
*/
protected function executeEveryTenLoops(InputInterface $input, OutputInterface $output): void
{
$output->writeln("Iteration " . $this->getLoopCount());
}
}
You also need to declare your command under the services:
# config/services
services:
# ... others declarations
App\Command\DaemonizedCommand:
parent: M6Web\Bundle\DaemonBundle\Command\DaemonCommand
tags:
- console.command
For information, you need to declare the autowire
and autoconfigure
parameters (to false
) only if you have defaults parameters for services (under _default
)
You can run a daemonized command as any other Symfony command with bin/console
. DaemonCommand parent class provide additional options :
--run-once
- Run the command just once--run-max
- Run the command x time--memory-max
- Gracefully stop running command when given memory volume, in bytes, is reached--shutdown-on-exception
- Ask for shutdown if an exception is thrown--show-exceptions
- Display exceptions on command output stream
Daemonized command trigger the following events :
DaemonEvents::DAEMON_START
DaemonEvents::DAEMON_LOOP_BEGIN
DaemonEvents::DAEMON_LOOP_EXCEPTION_STOP
DaemonEvents::DAEMON_LOOP_EXCEPTION_GENERAL
DaemonEvents::DAEMON_LOOP_MAX_MEMORY_REACHED
DaemonEvents::DAEMON_LOOP_ITERATION
DaemonEvents::DAEMON_LOOP_END
DaemonEvents::DAEMON_STOP