Spec 432: Exchange PowerShell production runner boundary and runtime gate. Validation: php artisan test --filter=Spec432 --compact; ./vendor/bin/pint --dirty --test --format agent; git diff --check. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #499
49 lines
1.5 KiB
PHP
49 lines
1.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\TenantConfiguration;
|
|
|
|
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
|
use Symfony\Component\Process\Process;
|
|
use Throwable;
|
|
|
|
final class SymfonyExchangePowerShellProcessExecutor implements ExchangePowerShellProcessExecutor
|
|
{
|
|
public function available(): bool
|
|
{
|
|
return class_exists(Process::class);
|
|
}
|
|
|
|
public function run(ExchangePowerShellProcessCommand $command): ExchangePowerShellProcessResult
|
|
{
|
|
$startedAt = microtime(true);
|
|
$process = null;
|
|
|
|
try {
|
|
$process = new Process($command->arguments, timeout: $command->timeoutSeconds);
|
|
$process->run();
|
|
|
|
return ExchangePowerShellProcessResult::completed(
|
|
exitCode: $process->getExitCode() ?? 1,
|
|
stdout: $process->getOutput(),
|
|
stderr: $process->getErrorOutput(),
|
|
durationMs: $this->durationMs($startedAt),
|
|
);
|
|
} catch (ProcessTimedOutException) {
|
|
if ($process instanceof Process && $process->isRunning()) {
|
|
$process->stop(0);
|
|
}
|
|
|
|
return ExchangePowerShellProcessResult::timedOut($this->durationMs($startedAt));
|
|
} catch (Throwable $exception) {
|
|
return ExchangePowerShellProcessResult::exception(class_basename($exception), $this->durationMs($startedAt));
|
|
}
|
|
}
|
|
|
|
private function durationMs(float $startedAt): int
|
|
{
|
|
return (int) max(0, round((microtime(true) - $startedAt) * 1000));
|
|
}
|
|
}
|