Install Dependencies
Ensure pcntl is enabled in your PHP binary. For Ubuntu/Debian:
sudo apt-get install php-pcntl
Restart your web server and PHP-FPM if applicable.
Add Bundle to composer.json
{
"require": {
"code-meme/daemon-bundle": "dev-master"
}
}
Run composer update.
Register Bundle
Add to config/bundles.php:
return [
// ...
CodeMeme\DaemonBundle\CodeMemeDaemonBundle::class => ['all' => true],
];
First Daemon Command
Create a console command extending DaemonCommand:
// src/Command/MyDaemonCommand.php
namespace App\Command;
use CodeMeme\DaemonBundle\Command\DaemonCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class MyDaemonCommand extends DaemonCommand
{
protected function execute(InputInterface $input, OutputInterface $output)
{
while (true) {
$output->writeln('Running daemon task...');
sleep(10);
}
}
}
Run the Daemon
php bin/console my:daemon
Daemonizing Existing Commands
Convert any Command into a daemon by extending DaemonCommand and overriding execute().
Example:
class BackgroundProcessorCommand extends DaemonCommand
{
protected function execute(InputInterface $input, OutputInterface $output)
{
while (!$this->isTerminated()) {
$this->processQueue();
sleep(5);
}
}
}
Signal Handling
Leverage built-in signal handling (e.g., SIGTERM, SIGINT) via isTerminated():
while (!$this->isTerminated()) {
// Long-running task
}
Logging
Use Symfony’s logger in execute():
$this->getLogger()->info('Daemon started');
Configuration
Inject services via constructor or getContainer():
public function __construct(private MyService $service) {}
Supervisor Integration Configure Supervisor to manage daemons (e.g., auto-restart on crash):
[program:my_daemon]
command=php /path/to/bin/console my:daemon
autostart=true
autorestart=true
user=www-data
pcntl Requirement
FatalErrorException: Class 'pcntl_signal' not found.php-pcntl is installed and enabled in php.ini (extension=pcntl).Permissions
setuid() or configure Supervisor to run as a specific user.sudo -u www-data php bin/console my:daemon to debug permission issues.Zombie Processes
terminate():
protected function terminate(InputInterface $input, OutputInterface $output)
{
posix_kill(posix_getpid(), SIGTERM);
}
Logging to File
$output = new StreamOutput(fopen('daemon.log', 'a'));
$this->execute($input, $output);
Debugging
debug flag to dump state:
if ($input->getOption('debug')) {
var_dump($this->getContainer()->getParameter('some_param'));
}
Graceful Shutdown
Override terminate() to handle cleanup:
protected function terminate(InputInterface $input, OutputInterface $output)
{
$this->getLogger()->info('Shutting down gracefully...');
// Close DB connections, release locks, etc.
}
Environment Awareness
Use APP_ENV to disable daemons in dev:
if ('dev' === $this->getContainer()->getParameter('kernel.environment')) {
throw new \RuntimeException('Daemons disabled in dev environment.');
}
Resource Limits
Daemons may hit memory limits. Use set_time_limit() or ini_set('memory_limit', '-1') cautiously.
Testing
Mock DaemonCommand in tests by extending it and overriding isTerminated():
$command = $this->getMockBuilder(MyDaemonCommand::class)
->onlyMethods(['isTerminated'])
->getMock();
$command->method('isTerminated')->willReturn(true);
Extending Functionality
System_Daemon behavior by overriding DaemonCommand::setupSignals().$this->getPidFile() to manage PID files:
$pidFile = $this->getPidFile();
if (file_exists($pidFile)) {
$output->writeln('Daemon already running!');
return 1;
}
How can I help you explore Laravel packages today?