fix(php8): statische Aufrufe, XAseco-Webaccess und TS3-count() fuer PHP 8 anpassen

- Php8Compat.php: nicht-statische Methoden, die als Klasse::methode()
  aufgerufen werden und kein $this nutzen, als static markieren
  (RemoteCP Core/Plugins, AdminServ Zip)
- web_access.inc.php: Response als array() statt '' initialisieren
  (behebt Illegal string offset in web_access, xmlrpc_db, mania_karma)
- plugin.teamspeak3.php: count() auf null entschaerft
This commit is contained in:
Patrick Asmus committed 2026-11-01 13:25:23 +01:00
1 parent b13b524ad5
commit 1ecb6c93b3
2 files changed
+165 -21

No files matched your search

+149 -21
View File
@@ -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
+16
View File
@@ -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)
# ============================================================