diff --git a/src/Internal/ProcessTrait.php b/src/Internal/ProcessTrait.php index 1208b5d..d5726da 100644 --- a/src/Internal/ProcessTrait.php +++ b/src/Internal/ProcessTrait.php @@ -24,7 +24,7 @@ trait ProcessTrait private string|null $group = null; private int $exitCode = 0; private LoggerInterface $logger; - private readonly string $socketFile; + private string $socketFile; public function run(WorkerContext $workerContext): int { diff --git a/src/Plugin/Scheduler/Scheduler.php b/src/Plugin/Scheduler/Scheduler.php new file mode 100644 index 0000000..5eb7fb2 --- /dev/null +++ b/src/Plugin/Scheduler/Scheduler.php @@ -0,0 +1,89 @@ +name === null && \is_string($this->command) => $this->command, + $this->name === null => 'closure', + 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 + )); + } + + $masterProcess->addWorker(new PeriodicProcess( + name: $name, + schedule: $this->schedule, + jitter: $this->jitter, + user: $this->user, + group: $this->group, + onStart: function (PeriodicProcess $worker) { + if (\is_string($this->command)) { + $worker->exec(...$this->prepareCommand($this->command)); + } 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); + } +}