97 lines
2.7 KiB
PHP
97 lines
2.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
final class PowerShellService
|
|
{
|
|
private string $psExe;
|
|
|
|
public function __construct(?string $psExe = null)
|
|
{
|
|
// Windows PowerShell 5.1
|
|
$this->psExe = $psExe ?? 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
// Alternative PowerShell 7:
|
|
// $this->psExe = $psExe ?? 'C:\\Program Files\\PowerShell\\7\\pwsh.exe';
|
|
}
|
|
|
|
/**
|
|
* @return array{exitCode:int, stdout:string, stderr:string}
|
|
*/
|
|
public function runScript(string $scriptPath, array $namedArgs, int $timeoutSeconds = 30): array
|
|
{
|
|
$scriptPath = realpath($scriptPath) ?: $scriptPath;
|
|
if (!is_file($scriptPath)) {
|
|
throw new RuntimeException("PowerShell script not found: {$scriptPath}");
|
|
}
|
|
|
|
$args = [];
|
|
foreach ($namedArgs as $name => $value) {
|
|
if (!preg_match('/^[A-Za-z][A-Za-z0-9]*$/', (string)$name)) {
|
|
throw new InvalidArgumentException("Invalid argument name: {$name}");
|
|
}
|
|
$args[] = '-' . $name;
|
|
$args[] = (string)$value; // wird als eigenes Argument übergeben (kein Command-String-Basteln)
|
|
}
|
|
|
|
$cmd = array_merge([
|
|
$this->psExe,
|
|
'-NoLogo',
|
|
'-NoProfile',
|
|
'-NonInteractive',
|
|
'-ExecutionPolicy', 'Bypass',
|
|
'-File', $scriptPath,
|
|
], $args);
|
|
|
|
$descriptors = [
|
|
0 => ["pipe", "r"],
|
|
1 => ["pipe", "w"],
|
|
2 => ["pipe", "w"],
|
|
];
|
|
|
|
$proc = proc_open($cmd, $descriptors, $pipes, null, null, [
|
|
'bypass_shell' => true,
|
|
'suppress_errors' => true,
|
|
]);
|
|
|
|
if (!is_resource($proc)) {
|
|
throw new RuntimeException("Failed to start PowerShell process.");
|
|
}
|
|
|
|
fclose($pipes[0]);
|
|
stream_set_blocking($pipes[1], false);
|
|
stream_set_blocking($pipes[2], false);
|
|
|
|
$stdout = '';
|
|
$stderr = '';
|
|
$start = time();
|
|
|
|
while (true) {
|
|
$stdout .= stream_get_contents($pipes[1]);
|
|
$stderr .= stream_get_contents($pipes[2]);
|
|
|
|
$status = proc_get_status($proc);
|
|
if (!$status['running']) {
|
|
break;
|
|
}
|
|
|
|
if ((time() - $start) > $timeoutSeconds) {
|
|
proc_terminate($proc, 9);
|
|
$stderr .= "\n[PHP] Timeout after {$timeoutSeconds}s";
|
|
break;
|
|
}
|
|
|
|
usleep(50_000);
|
|
}
|
|
|
|
fclose($pipes[1]);
|
|
fclose($pipes[2]);
|
|
|
|
$exitCode = proc_close($proc);
|
|
|
|
return [
|
|
'exitCode' => (int)$exitCode,
|
|
'stdout' => trim($stdout),
|
|
'stderr' => trim($stderr),
|
|
];
|
|
}
|
|
}
|