Files
Sven 9de2358480 Portiere PHP-Cronjobs auf Spring-Tasks und ergänze Legacy-Tests
Migriert die weiterhin benötigten Zeitaufgaben einschließlich SOAP-Importen, Betriebsstatus und Wiederanlaufkontrolle. Aktiviert 115 native Spring-Tasks im Docker-Image ohne Cron-Daemon oder PHP und dokumentiert die entfallenen HHA-Aufrufe. Ergänzt PHP-/UI-Testwerkzeuge und aktualisiert den Projektstand auf Version 0.2.0.
2026-10-27 09:53:23 +01:00

95 lines
4.1 KiB
PHP

<?php
/** Load only named top-level functions, never the application's startup code. */
final class LegacyFunctions
{
public static function load(string $root, array $selection): array
{
$manifest = [];
foreach ($selection as $relative => $names) {
$file = $root . '/' . $relative;
if (!is_file($file) || !is_readable($file)) {
throw new RuntimeException('Missing readable source: ' . $file);
}
$source = file_get_contents($file);
$functions = self::extract($source, $names);
foreach ($functions as $name => $definition) {
if (function_exists($name)) {
throw new RuntimeException('Function already defined: ' . $name);
}
// Preserve the source bytes and weak scalar coercion of the legacy code.
eval($definition['code']);
$manifest[$relative]['functions'][$name] = [
'line' => $definition['line'],
'sha256' => hash('sha256', $definition['code']),
];
}
$manifest[$relative]['sha256'] = hash('sha256', $source);
}
return $manifest;
}
public static function extract(string $source, array $names): array
{
$tokens = token_get_all($source);
$found = [];
$depth = 0;
for ($i = 0, $count = count($tokens); $i < $count; $i++) {
$token = $tokens[$i];
if ($depth === 0 && is_array($token) && $token[0] === T_FUNCTION) {
$j = $i + 1;
while (isset($tokens[$j]) && is_array($tokens[$j]) &&
in_array($tokens[$j][0], [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT,
T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG], true)) {
$j++;
}
$name = is_array($tokens[$j] ?? null) && $tokens[$j][0] === T_STRING
? $tokens[$j][1] : null;
if ($name !== null && in_array($name, $names, true)) {
if (isset($found[$name])) {
throw new RuntimeException('Duplicate function: ' . $name);
}
$code = '';
$bodyDepth = 0;
$started = false;
for ($j = $i; $j < $count; $j++) {
$part = $tokens[$j];
$code .= is_array($part) ? $part[1] : $part;
if (is_array($part) && in_array($part[0], [T_INCLUDE, T_INCLUDE_ONCE,
T_REQUIRE, T_REQUIRE_ONCE, T_EVAL, T_EXIT], true)) {
throw new RuntimeException('Unsafe startup/exit operation in ' . $name);
}
if ($part === '{' || (is_array($part) && in_array($part[0],
[T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true))) {
$bodyDepth++;
$started = true;
} elseif ($part === '}') {
$bodyDepth--;
if ($started && $bodyDepth === 0) {
$found[$name] = ['code' => $code, 'line' => $token[2]];
$i = $j;
break;
}
}
}
if (!isset($found[$name])) {
throw new RuntimeException('Incomplete function: ' . $name);
}
continue;
}
}
if ($token === '{' || (is_array($token) && in_array($token[0],
[T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true))) {
$depth++;
} elseif ($token === '}') {
$depth--;
}
}
$missing = array_diff($names, array_keys($found));
if ($missing) {
throw new RuntimeException('Missing top-level function(s): ' . implode(', ', $missing));
}
return $found;
}
}