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.
This commit is contained in:
1 parent
781a2b82dc
commit
9de2358480
57 files changed
+3251
-7
No files matched your search
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
final class FixtureState
|
||||
{
|
||||
public static array $parameters = [];
|
||||
public static array $serviceCalls = [];
|
||||
public static array $writes = [];
|
||||
public static array $logs = [];
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$parameters = [];
|
||||
self::$serviceCalls = [];
|
||||
self::$writes = [];
|
||||
self::$logs = [];
|
||||
$GLOBALS['db'] = new FixtureDatabase();
|
||||
$GLOBALS['hq_id'] = 7;
|
||||
$GLOBALS['phpVersion'] = '8.2';
|
||||
$GLOBALS['ph'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
final class FixtureDatabase
|
||||
{
|
||||
public array $expected = [];
|
||||
|
||||
public function expect(string $method, string $sql, mixed $result): void
|
||||
{
|
||||
$this->expected[] = [$method, $sql, $result];
|
||||
}
|
||||
|
||||
public function __call(string $method, array $arguments): mixed
|
||||
{
|
||||
if (!$this->expected) {
|
||||
throw new RuntimeException('Unexpected database operation: ' . $method);
|
||||
}
|
||||
[$expectedMethod, $expectedSql, $result] = array_shift($this->expected);
|
||||
if ($method !== $expectedMethod || $arguments !== [$expectedSql]) {
|
||||
throw new RuntimeException('Unexpected fixture query: ' . $method . ' ' . ($arguments[0] ?? ''));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
final class FixtureRows
|
||||
{
|
||||
private int $index = 0;
|
||||
public bool $freed = false;
|
||||
public function __construct(private array $rows) {}
|
||||
public function fetch_assoc(): ?array { return $this->rows[$this->index++] ?? null; }
|
||||
public function fetchRow(): ?array { return $this->fetch_assoc(); }
|
||||
public function free(): void { $this->freed = true; }
|
||||
}
|
||||
|
||||
final class DB
|
||||
{
|
||||
public static function isError(mixed $value): bool { return $value instanceof Throwable; }
|
||||
}
|
||||
|
||||
function getParameterValue($employee, $key, $hq)
|
||||
{
|
||||
if (!in_array($key, ['DB_FIELDTYPE_WRAPPER_1', 'COUNTRY_FON_PREFIX',
|
||||
'INV_PRINT_DISCOUNT', 'INV_USE_EURO_SIGN', 'MASK_MARKUP_MODE'], true)) {
|
||||
throw new RuntimeException('Unexpected parameter: ' . $key);
|
||||
}
|
||||
return FixtureState::$parameters[$hq][$key] ?? '';
|
||||
}
|
||||
|
||||
function getServiceValues(...$arguments)
|
||||
{
|
||||
if (!FixtureState::$serviceCalls) {
|
||||
throw new RuntimeException('Unexpected service lookup');
|
||||
}
|
||||
[$expected, $result] = array_shift(FixtureState::$serviceCalls);
|
||||
if ($arguments !== $expected) {
|
||||
throw new RuntimeException('Service lookup arguments differ: ' . json_encode($arguments));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function insertStmt($table, $row)
|
||||
{
|
||||
if ($table !== 'phoenix_log.b2b_objects') {
|
||||
throw new RuntimeException('Unexpected write target: ' . $table);
|
||||
}
|
||||
FixtureState::$writes[] = [$table, $row];
|
||||
}
|
||||
|
||||
function writeLog_($file, $message) { FixtureState::$logs[] = [$file, $message]; }
|
||||
function reportDie($message) { throw new RuntimeException($message); }
|
||||
@@ -0,0 +1,94 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
abstract class LegacyTestCase extends PHPUnit\Framework\TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
FixtureState::reset();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
self::assertSame([], $GLOBALS['db']->expected, 'Unconsumed database expectations');
|
||||
self::assertSame([], FixtureState::$serviceCalls, 'Unconsumed service expectations');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user