Skip to content

Commit

Permalink
Moved shared code for plugins in traits
Browse files Browse the repository at this point in the history
  • Loading branch information
luzrain committed Sep 10, 2024
1 parent eeb8979 commit a395b1d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 71 deletions.
16 changes: 16 additions & 0 deletions src/Plugin/NullStop.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Luzrain\PHPStreamServer\Plugin;

use Amp\Future;
use function Amp\async;

trait NullStop
{
public function stop(): Future
{
return async(static fn() => null);
}
}
38 changes: 38 additions & 0 deletions src/Plugin/PcntlExecCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Luzrain\PHPStreamServer\Plugin;

use Luzrain\PHPStreamServer\Server;

trait PcntlExecCommand
{
/**
* Prepare command for pcntl_exec acceptable format
*
* @return array{0: string, 1: list<string>}
*/
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];
}
}
51 changes: 9 additions & 42 deletions src/Plugin/Scheduler/Scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -34,56 +36,21 @@ 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,
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));
onStart: function (PeriodicProcess $worker) use ($pcntlExec) {
if ($pcntlExec !== null) {
$worker->exec(...$pcntlExec);
} else {
($this->command)($worker);
}
},
));
}

/**
* @return array{0: string, 1: list<string>}
*/
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);
}
}
39 changes: 10 additions & 29 deletions src/Plugin/Supervisor/Supervisor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -34,44 +37,22 @@ 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,
reloadable: $this->reloadable,
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<string>}
*/
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);
}
}

0 comments on commit a395b1d

Please sign in to comment.