PHP_AdminTool_Projekt/app/Services/PowerShellService.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),
];
}
}