diff --git a/src/Plugin/NullStop.php b/src/Plugin/NullStop.php new file mode 100644 index 0000000..3b7023a --- /dev/null +++ b/src/Plugin/NullStop.php @@ -0,0 +1,16 @@ + null); + } +} diff --git a/src/Plugin/PcntlExecCommand.php b/src/Plugin/PcntlExecCommand.php new file mode 100644 index 0000000..5799802 --- /dev/null +++ b/src/Plugin/PcntlExecCommand.php @@ -0,0 +1,38 @@ +} + */ + private function prepareCommandForPcntlExec(string $command): array + { + // Check if command contains logic operators such as && and || + if (\preg_match('/(\'[^\']*\'|"[^"]*")(*SKIP)(*FAIL)|&&|\|\|/', $command) === 1) { + throw new \RuntimeException(\sprintf( + '%s does not directly support executing multiple commands with logical operators. Use shell with -c option e.g. "/bin/sh -c "%s"', + Server::NAME, + $command, + )); + } + + \preg_match_all('/\'[^\']*\'|"[^"]*"|\S+/', $command, $matches); + $parts = \array_map(static fn (string $part): string => \trim($part, '"\''), $matches[0]); + $binary = \array_shift($parts); + $args = $parts; + + if (!\str_starts_with($binary, '/') && \is_string($absoluteBinaryPath = \shell_exec("command -v $binary"))) { + $binary = \trim($absoluteBinaryPath); + } + + return [$binary, $args]; + } +} diff --git a/src/Plugin/Scheduler/Scheduler.php b/src/Plugin/Scheduler/Scheduler.php index 5eb7fb2..6e2f1e5 100644 --- a/src/Plugin/Scheduler/Scheduler.php +++ b/src/Plugin/Scheduler/Scheduler.php @@ -4,15 +4,17 @@ namespace Luzrain\PHPStreamServer\Plugin\Scheduler; -use Amp\Future; use Luzrain\PHPStreamServer\Internal\MasterProcess; use Luzrain\PHPStreamServer\PeriodicProcess; +use Luzrain\PHPStreamServer\Plugin\PcntlExecCommand; +use Luzrain\PHPStreamServer\Plugin\NullStop; use Luzrain\PHPStreamServer\Plugin\PluginInterface; -use Luzrain\PHPStreamServer\Server; -use function Amp\async; final readonly class Scheduler implements PluginInterface { + use NullStop; + use PcntlExecCommand; + /** * @param string|\Closure(PeriodicProcess): void $command bash command as string or php closure */ @@ -34,13 +36,7 @@ public function start(MasterProcess $masterProcess): void default => $this->name, }; - if (\is_string($this->command) && $this->isCommandContainsLogicOperators($this->command)) { - throw new \RuntimeException(\sprintf( - '%s does not directly support executing multiple commands with logical operators. Use shell with -c option e.g. "/bin/sh -c "%s"', - Server::NAME, - $this->command - )); - } + $pcntlExec = \is_string($this->command) ? $this->prepareCommandForPcntlExec($this->command) : null; $masterProcess->addWorker(new PeriodicProcess( name: $name, @@ -48,42 +44,13 @@ public function start(MasterProcess $masterProcess): void jitter: $this->jitter, user: $this->user, group: $this->group, - onStart: function (PeriodicProcess $worker) { - if (\is_string($this->command)) { - $worker->exec(...$this->prepareCommand($this->command)); + onStart: function (PeriodicProcess $worker) use ($pcntlExec) { + if ($pcntlExec !== null) { + $worker->exec(...$pcntlExec); } else { ($this->command)($worker); } }, )); } - - /** - * @return array{0: string, 1: list} - */ - private function prepareCommand(string $command): array - { - \preg_match_all('/\'[^\']*\'|"[^"]*"|\S+/', $command, $matches); - $parts = \array_map(static fn (string $part) => \trim($part, '"\''), $matches[0]); - $binary = \array_shift($parts); - $args = $parts; - - if (!\str_starts_with($binary, '/')) { - if (\is_string($absoluteBinaryPath = \shell_exec("command -v $binary"))) { - $binary = \trim($absoluteBinaryPath); - } - } - - return [$binary, $args]; - } - - private function isCommandContainsLogicOperators(string $command): bool - { - return \preg_match('/(\'[^\']*\'|"[^"]*")(*SKIP)(*FAIL)|&&|\|\|/', $command) === 1; - } - - public function stop(): Future - { - return async(static fn() => null); - } } diff --git a/src/Plugin/Supervisor/Supervisor.php b/src/Plugin/Supervisor/Supervisor.php index 2845413..9e96307 100644 --- a/src/Plugin/Supervisor/Supervisor.php +++ b/src/Plugin/Supervisor/Supervisor.php @@ -4,14 +4,17 @@ namespace Luzrain\PHPStreamServer\Plugin\Supervisor; -use Amp\Future; use Luzrain\PHPStreamServer\Internal\MasterProcess; +use Luzrain\PHPStreamServer\Plugin\PcntlExecCommand; +use Luzrain\PHPStreamServer\Plugin\NullStop; use Luzrain\PHPStreamServer\Plugin\PluginInterface; use Luzrain\PHPStreamServer\WorkerProcess; -use function Amp\async; final readonly class Supervisor implements PluginInterface { + use NullStop; + use PcntlExecCommand; + /** * @param string|\Closure(WorkerProcess): void $command bash command as string or php closure */ @@ -34,6 +37,8 @@ public function start(MasterProcess $masterProcess): void default => $this->name, }; + $pcntlExec = \is_string($this->command) ? $this->prepareCommandForPcntlExec($this->command) : null; + $masterProcess->addWorker(new WorkerProcess( name: $name, count: $this->count, @@ -41,37 +46,13 @@ public function start(MasterProcess $masterProcess): void restartDelay: $this->restartDelay, user: $this->user, group: $this->group, - onStart: function (WorkerProcess $worker) { - if (\is_string($this->command)) { - $worker->exec(...$this->prepareCommand($this->command)); + onStart: function (WorkerProcess $worker) use ($pcntlExec) { + if ($pcntlExec !== null) { + $worker->exec(...$pcntlExec); } else { ($this->command)($worker); } }, )); } - - /** - * @return array{0: string, 1: list} - */ - private function prepareCommand(string $command): array - { - \preg_match_all('/\'[^\']*\'|"[^"]*"|\S+/', $command, $matches); - $parts = \array_map(static fn (string $part) => \trim($part, '"\''), $matches[0]); - $binary = \array_shift($parts); - $args = $parts; - - if (!\str_starts_with($binary, '/')) { - if (\is_string($absoluteBinaryPath = \shell_exec("command -v $binary"))) { - $binary = \trim($absoluteBinaryPath); - } - } - - return [$binary, $args]; - } - - public function stop(): Future - { - return async(static fn() => null); - } }