germanovn / php-overdaemon
PhpOverDaemon — daemon management solution written in PHP
Requires
- php: >=8.2.0
- ext-pcntl: *
- psr/log: ^3.0.0
Requires (Dev)
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2025-05-29 01:47:45 UTC
README
Всем привет! В процессе интеграции с брокерами сообщений мне пришлось демонизировать некоторые куски кода на PHP. Этот репозиторий содержит пакет подключаемый с помощью Composer. Цель пакета - поддержание написанных вами демонов в рабочем состоянии, а так же управление ими и сбор логов.
Установка
composer require germanovn/php-overdaemon
Использование
Для использования OverDaemon необходимо:
- создать класс репозитория демонов реализующий интерфейс библиотеки
GermanovN\OverDaemon\DaemonGate\InferiorDaemonRepository
; - иметь демона, над которым вы хотите иметь больший контроль.
Примеры
Ваш репозиторий демонов может выглядеть так:
<?php namespace MyApp\Daemon; use GermanovN\OverDaemon\DaemonGate\InferiorDaemon; use GermanovN\OverDaemon\DaemonGate\InferiorDaemonRepository; class MyInferiorDaemonRepository implements InferiorDaemonRepository { private array $daemonCollection; public function getAll(): array { return $this->daemonCollection; } public function add(InferiorDaemon $daemon): void { $this->daemonCollection[] = $daemon; } public function count(): int { return count($this->daemonCollection); } }
Ваш демон может выглядеть так:
<?php namespace MyApp\Daemon; use Exception; use GermanovN\OverDaemon\Daemon\Daemon; use GermanovN\OverDaemon\Daemon\StoppableDaemon; use GermanovN\OverDaemon\DaemonGate\InferiorDaemon; class MyDaemon extends StoppableDaemon implements InferiorDaemon { private int $pid; /** * @throws Exception */ public function devour(array $args = null): int { $this->pid = $args['pid']; try { // бесконечный цикл выполнения while ($this->isRunningDaemon()) { // получение задачи $execTime = $this->getSomeLongTask(); if (null === $execTime) { $this->stopDaemon(SIGSTOP); continue; } // выполнение задачи $this->logger->debug("Start '" . $this->name() ."' PID: {$this->pid}"); $this->doTask($execTime); $this->logger->debug("End '" . $this->name() ."' PID: {$this->pid}"); } return Daemon::EXIT_CODE_SUCCESS; } catch (Exception $e) { $this->logger->error( sprintf('Code: %d. Message: %s', $e->getCode(), $e->getMessage()), $e->getTrace() ); return Daemon::EXIT_CODE_ERROR; } } /** @throws Exception */ private function getSomeLongTask(): ?int { $execTime = random_int(1, 10) * 10; return $execTime >= 90 ? null : $execTime; } private function doTask(int $task): void { for ($i = 0; $i < $task; $i++) { $this->logger->debug($this->name() . " PID: {$this->pid} " . $i); usleep(10000); } } public function beforeDevour(): bool { // в это методе можно обновить подключение к вашей БД return true; } public function afterDevour(): void { } public function name(): string { return self::class; } }
Обратите внимание в примере используется, абстрактный класс \GermanovN\OverDaemon\Daemon\StoppableDaemon
.
Этот класс существует для реализации корректного завершения работы при получении POSIX сигналов.
По-умолчанию обрабатывает сигналы:
- SIGINT - Сигнал прерывания (Ctrl-C) с терминала;
- SIGTERM - Сигнал завершения (сигнал по умолчанию для утилиты kill).
Пример использования OverDaemon:
<?php // src/Command/OverDaemonCommand.php // Перехват сигналов pcntl_async_signals(true); // Передача обработки сигналов в SigHandler $sigHandler = SigHandler::instance(); pcntl_signal(SIGINT, [$sigHandler, 'handle']); pcntl_signal(SIGTERM, [$sigHandler, 'handle']); pcntl_signal(SIGHUP, [$sigHandler, 'handle']); $repository = new MyInferiorDaemonRepository(); $repository->add(new MyDaemon()); $daemon = new OverDaemon( new DaemonConfig(), $repository, $sigHandler ); echo $daemon->devour();
Запуск тестов
vendor\bin\phpunit tests