diff --git a/assets/bin/Php8Compat.php b/assets/bin/Php8Compat.php index d44ee6f..a3c752f 100644 --- a/assets/bin/Php8Compat.php +++ b/assets/bin/Php8Compat.php @@ -16,6 +16,11 @@ * Ausserdem werden String-Offsets mit geschweiften Klammern ($str{0}) auf * eckige Klammern umgestellt (unter PHP 8 ein Parse-Error). * + * Nicht-statische Methoden, die statisch aufgerufen werden (Core::getObject()), + * sind unter PHP 8 ein Fatal Error. Solche Methoden werden als static markiert, + * sofern sie $this nicht verwenden und keine Unterklasse sie nicht-statisch + * ueberschreibt. + * * Idempotent: Klassen mit vorhandenem __construct() werden uebersprungen. * Dateien werden in-place geschrieben (Besitzer und Rechte bleiben erhalten). */ @@ -25,32 +30,155 @@ if ($argc < 2) { exit(1); } -$totalFiles = 0; -$totalClasses = 0; -$totalCurly = 0; - +$files = array(); for ($i = 1; $i < $argc; $i++) { - $dir = $argv[$i]; - if (!is_dir($dir)) continue; - $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)); + if (!is_dir($argv[$i])) continue; + $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($argv[$i], FilesystemIterator::SKIP_DOTS)); foreach ($it as $file) { - if (strtolower($file->getExtension()) !== 'php') continue; - $path = $file->getPathname(); - $src = file_get_contents($path); - if ($src === false) continue; - list($new, $count) = patchConstructors($src); - list($new, $curly) = patchCurlyOffsets($new); - if ($new !== $src) { - file_put_contents($path, $new); - $totalFiles++; - $totalClasses += $count; - $totalCurly += $curly; - echo " $path: $count Konstruktor(en), $curly Offset(s)\n"; - } + if (strtolower($file->getExtension()) === 'php') $files[] = $file->getPathname(); } } -echo " PHP-8-Kompatibilitaet: $totalClasses Konstruktor(en), $totalCurly Offset(s) in $totalFiles Datei(en) angepasst.\n"; +$changed = array(); +$totalClasses = 0; +$totalCurly = 0; +$totalStatic = 0; + +// Durchlauf 1: Konstruktoren und String-Offsets (pro Datei) +foreach ($files as $path) { + $src = file_get_contents($path); + if ($src === false) continue; + list($new, $count) = patchConstructors($src); + list($new, $curly) = patchCurlyOffsets($new); + if ($new !== $src) { + file_put_contents($path, $new); + $changed[$path] = true; + $totalClasses += $count; + $totalCurly += $curly; + } +} + +// Durchlauf 2: statisch aufgerufene Methoden (dateiuebergreifend) +foreach (patchStaticCalls($files) as $path => $count) { + $changed[$path] = true; + $totalStatic += $count; +} + +echo " PHP-8-Kompatibilitaet: $totalClasses Konstruktor(en), $totalCurly Offset(s), $totalStatic static-Methode(n) in " . count($changed) . " Datei(en) angepasst.\n"; + +/** + * Markiert nicht-statische Methoden als static, wenn sie irgendwo als + * Klasse::methode() aufgerufen werden, $this nicht verwenden und von keiner + * Unterklasse nicht-statisch ueberschrieben werden. + * + * Liefert [Pfad => Anzahl geaenderter Methoden]. + */ +function patchStaticCalls($files) +{ + $methods = array(); // "klasse::methode" => [pfad, token-index, static, usesThis] + $parents = array(); // "klasse" => "elternklasse" + $calls = array(); // "klasse::methode" => true + $tokensByFile = array(); + + foreach ($files as $path) { + $src = file_get_contents($path); + if ($src === false || strpos($src, '::') === false && stripos($src, 'class') === false) continue; + $t = @token_get_all($src); + $tokensByFile[$path] = $t; + $n = count($t); + $depth = 0; + $cls = null; + $clsDepth = -1; + for ($i = 0; $i < $n; $i++) { + $x = $t[$i]; + if ($x === '{' || (is_array($x) && in_array($x[0], array(T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES), true))) { + $depth++; + } elseif ($x === '}') { + $depth--; + if ($depth === $clsDepth) { $cls = null; $clsDepth = -1; } + } elseif (is_array($x) && $x[0] === T_CLASS) { + $prev = prevSignificant($t, $i); + if ($prev !== null && is_array($t[$prev]) && in_array($t[$prev][0], array(T_DOUBLE_COLON, T_NEW), true)) continue; + $j = nextSignificant($t, $i); + if ($j !== null && is_array($t[$j]) && $t[$j][0] === T_STRING) { + $cls = strtolower($t[$j][1]); + $clsDepth = $depth; + $e = nextSignificant($t, $j); + if ($e !== null && is_array($t[$e]) && $t[$e][0] === T_EXTENDS) { + $pc = nextSignificant($t, $e); + if ($pc !== null && is_array($t[$pc])) $parents[$cls] = strtolower(ltrim($t[$pc][1], '\\')); + } + } + } elseif ($cls !== null && is_array($x) && $x[0] === T_FUNCTION && $depth === $clsDepth + 1) { + $static = false; + for ($k = $i - 1; $k >= 0; $k--) { + if (is_array($t[$k]) && $t[$k][0] === T_STATIC) $static = true; + if ($t[$k] === ';' || $t[$k] === '{' || $t[$k] === '}') break; + } + $j = nextSignificant($t, $i); + if ($j !== null && $t[$j] === '&') $j = nextSignificant($t, $j); + if ($j === null || !is_array($t[$j])) continue; + $m = strtolower($t[$j][1]); + $usesThis = false; + $k = $j; + while ($k < $n && $t[$k] !== '{' && $t[$k] !== ';') $k++; + if ($k < $n && $t[$k] === '{') { + $d = 0; + for ($q = $k; $q < $n; $q++) { + $y = $t[$q]; + if ($y === '{' || (is_array($y) && in_array($y[0], array(T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES), true))) $d++; + elseif ($y === '}') { $d--; if ($d === 0) break; } + elseif (is_array($y) && $y[0] === T_VARIABLE && $y[1] === '$this') $usesThis = true; + } + } + $methods["$cls::$m"] = array($path, $i, $static, $usesThis); + } elseif (is_array($x) && $x[0] === T_DOUBLE_COLON && $i > 0 && isset($t[$i + 1])) { + $p = $t[$i - 1]; + $nx = $t[$i + 1]; + if (is_array($p) && $p[0] === T_STRING && is_array($nx) && $nx[0] === T_STRING) { + $j = nextSignificant($t, $i + 1); + $c = strtolower($p[1]); + if ($j !== null && $t[$j] === '(' && !in_array($c, array('parent', 'self', 'static'), true)) { + $calls[$c . '::' . strtolower($nx[1])] = true; + } + } + } + } + } + + // Kandidaten je Datei sammeln (Token-Index der function-Deklaration) + $todo = array(); + foreach ($calls as $key => $_) { + if (!isset($methods[$key])) continue; + list($path, $idx, $static, $usesThis) = $methods[$key]; + if ($static || $usesThis) continue; + // Nicht anfassen, wenn eine Unterklasse die Methode nicht-statisch ueberschreibt + list($target, $m) = explode('::', $key); + $overridden = false; + foreach ($methods as $other => $info) { + list($oc, $om) = explode('::', $other); + if ($om !== $m || $oc === $target || $info[2]) continue; + for ($a = $oc, $guard = 0; isset($parents[$a]) && $guard < 20; $a = $parents[$a], $guard++) { + if ($parents[$a] === $target) { $overridden = true; break 2; } + } + } + if ($overridden) continue; + $todo[$path][] = $idx; + } + + $result = array(); + foreach ($todo as $path => $indexes) { + $t = $tokensByFile[$path]; + foreach ($indexes as $idx) { + $t[$idx] = array(T_FUNCTION, 'static ' . $t[$idx][1], 0); + } + $out = ''; + foreach ($t as $tok) $out .= is_array($tok) ? $tok[1] : $tok; + file_put_contents($path, $out); + $result[$path] = count($indexes); + } + return $result; +} /** * Ersetzt String-Offsets mit geschweiften Klammern ($str{0}) durch eckige diff --git a/assets/bin/RunTrackmaniaServer.sh b/assets/bin/RunTrackmaniaServer.sh index 919ce40..80e6489 100644 --- a/assets/bin/RunTrackmaniaServer.sh +++ b/assets/bin/RunTrackmaniaServer.sh @@ -426,6 +426,22 @@ if [ -f "$XASECO_MODERNIZER" ] && ! grep -q 'mysqli_report' "$XASECO_MODERNIZER" echo " modernizer.inc.php: mysqli_report(MYSQLI_REPORT_OFF) ergaenzt." fi +# XAseco: Webaccess initialisiert Antworten als '' statt array(). Schreib- und +# Lesezugriffe wie $response['Code'] waren unter PHP 7 nur Warnings +# ("Illegal string offset"), unter PHP 8 beenden sie XAseco (TypeError). +XASECO_WEBACCESS="/opt/tmserver/xaseco/includes/web_access.inc.php" +if [ -f "$XASECO_WEBACCESS" ] && grep -q "\['Response'\] = '';" "$XASECO_WEBACCESS"; then + sed -i "s/\['Response'\] = '';/['Response'] = array();/" "$XASECO_WEBACCESS" + echo " web_access.inc.php: Response als array() initialisiert." +fi + +# XAseco TeamSpeak3-Plugin: count() auf null ist unter PHP 8 ein TypeError +XASECO_TS3="/opt/tmserver/xaseco/plugins/plugin.teamspeak3.php" +if [ -f "$XASECO_TS3" ] && grep -qF "if ( count(\$info['players']) > 0 ) {" "$XASECO_TS3"; then + sed -i "s/if ( count(\$info\['players'\]) > 0 ) {/if ( !empty(\$info['players']) \&\& is_array(\$info['players']) ) {/" "$XASECO_TS3" + echo " plugin.teamspeak3.php: count()-Pruefung entschaerft." +fi + # ============================================================ # XAseco: TeamSpeak3-Plugin Gateway aktualisieren (fuer bestehende Volumes) # ============================================================