aboutcoders/process-control-bundle
Symfony bundle providing process control via a container service (abc.process_control.controller). Default PCNTL implementation listens for SIGTERM so long-running jobs can detect when to exit. Service registration can be disabled via configuration.
Installation:
composer require aboutcoders/process-control-bundle
Add the bundle to config/bundles.php (Symfony 4+):
return [
// ...
Abc\Bundle\ProcessControlBundle\AbcProcessControlBundle::class => ['all' => true],
];
First Use Case: Inject the controller into a service or command to handle graceful shutdowns:
use Abc\Bundle\ProcessControlBundle\ProcessControl\ProcessControlControllerInterface;
class MyCommand extends Command
{
private $processControl;
public function __construct(ProcessControlControllerInterface $processControl)
{
$this->processControl = $processControl;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($this->processControl->doExit()) {
$output->writeln('Received SIGTERM, exiting gracefully...');
return Command::SUCCESS;
}
// Normal execution...
}
}
Graceful Shutdown Handling:
Use doExit() in long-running processes (e.g., CLI commands, background jobs) to detect SIGTERM and perform cleanup:
if ($processControl->doExit()) {
$this->cleanupResources();
exit(0);
}
Integration with Symfony Events: Dispatch events before shutdown:
if ($processControl->doExit()) {
$this->eventDispatcher->dispatch(new PreShutdownEvent());
return Command::SUCCESS;
}
Custom Signal Handling:
Extend the bundle’s ProcessControlController to handle additional signals (e.g., SIGINT):
class CustomProcessControl extends ProcessControlController
{
public function doExit(): bool
{
return parent::doExit() || $this->isSignalReceived(SIGINT);
}
}
Register it as a service:
services:
abc.process_control.controller:
class: App\Service\CustomProcessControl
Background Job Cleanup: Use in queue workers (e.g., Symfony Messenger) to abort processing:
public function __invoke(JobMessage $message)
{
if ($this->processControl->doExit()) {
throw new ProcessTerminatedException('Worker shutting down');
}
// Process job...
}
Service Unavailability:
If register_controller: false is set in config/packages/aboutcoders_process_control.yaml, the service won’t exist. Verify configuration:
abc_process_control:
register_controller: true # Default, ensure this is set
Signal Race Conditions:
doExit() may return false briefly after SIGTERM is sent. Use a loop for critical sections:
while (!$processControl->doExit()) {
// Critical work...
}
Testing Limitations: Signals can’t be simulated in unit tests. Use mocks:
$mockController = $this->createMock(ProcessControlControllerInterface::class);
$mockController->method('doExit')->willReturn(true);
PCNTL Dependency:
The bundle relies on PHP’s pcntl extension. Ensure it’s enabled in php.ini:
extension=pcntl
Fallback: Use a custom implementation (e.g., for Docker containers with --init).
$controller->getSignalStatus(); // Returns array of received signals
php bin/console debug:container abc.process_control.controller
Custom Process Control:
Implement ProcessControlControllerInterface for alternative backends (e.g., HTTP-based shutdown hooks):
class HttpProcessControl implements ProcessControlControllerInterface
{
public function doExit(): bool
{
return $this->httpClient->get('/shutdown')->getStatusCode() === 200;
}
}
Configuration Overrides:
Override the default service in config/services.yaml:
services:
abc.process_control.controller:
class: App\Service\CustomProcessControl
arguments:
$signalHandler: '@custom_signal_handler'
Event-Driven Shutdowns: Dispatch Symfony events before shutdown:
if ($processControl->doExit()) {
$this->eventDispatcher->dispatch(new ShutdownEvent($this->getResources()));
}
How can I help you explore Laravel packages today?