chore: complete update
This commit is contained in:
218
src/Framework/Core/ClassParser.php
Normal file
218
src/Framework/Core/ClassParser.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Core;
|
||||
|
||||
/**
|
||||
* Parser für PHP-Klassendateien ohne externe Abhängigkeiten
|
||||
*/
|
||||
final class ClassParser
|
||||
{
|
||||
private static array $classCache = [];
|
||||
private static array $tokenCache = [];
|
||||
|
||||
/**
|
||||
* Gibt alle Klassen, Interfaces und Traits in einer Datei zurück
|
||||
*
|
||||
* @param string $file Pfad zur PHP-Datei
|
||||
* @return array Namen der in der Datei enthaltenen Klassen
|
||||
*/
|
||||
public static function getClassesInFile(string $file): array
|
||||
{
|
||||
// Prüfe Cache
|
||||
$cacheKey = md5($file . filemtime($file));
|
||||
if (isset(self::$classCache[$cacheKey])) {
|
||||
return self::$classCache[$cacheKey];
|
||||
}
|
||||
|
||||
$classes = [];
|
||||
$namespace = '';
|
||||
$tokens = self::getTokens($file);
|
||||
|
||||
if ($tokens === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$count = count($tokens);
|
||||
$i = 0;
|
||||
|
||||
// Durchlaufe alle Tokens
|
||||
while ($i < $count) {
|
||||
if ($tokens[$i][0] === T_NAMESPACE) {
|
||||
// Namespace finden
|
||||
$namespace = self::parseNamespace($tokens, $i);
|
||||
#debug("Found namespace: '$namespace' in file: $file");
|
||||
} elseif ($tokens[$i][0] === T_CLASS || $tokens[$i][0] === T_INTERFACE || $tokens[$i][0] === T_TRAIT) {
|
||||
// Klasse/Interface/Trait finden
|
||||
$className = self::parseClassName($tokens, $i);
|
||||
if ($className !== null) {
|
||||
#debug("Found class: '$className', current namespace: '$namespace'");
|
||||
|
||||
if (empty($namespace)) {
|
||||
// Fallback: Versuche Namespace aus Dateiinhalt zu extrahieren
|
||||
$namespace = self::extractNamespaceFromContent($file);
|
||||
#debug("Fallback namespace from content: '$namespace'");
|
||||
}
|
||||
|
||||
if (!empty($namespace)) {
|
||||
$fullClassName = '\\' . trim($namespace, '\\') . '\\' . $className;
|
||||
} else {
|
||||
$fullClassName = '\\' . $className;
|
||||
}
|
||||
|
||||
#debug("Final class name: '$fullClassName'");
|
||||
$classes[] = $fullClassName;
|
||||
}
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
// Ergebnis cachen
|
||||
self::$classCache[$cacheKey] = $classes;
|
||||
|
||||
#debug($classes);
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parst den Namespace aus den Tokens
|
||||
*/
|
||||
private static function parseNamespace(array $tokens, int &$i): string
|
||||
{
|
||||
$namespace = '';
|
||||
$originalI = $i;
|
||||
$i++; // T_NAMESPACE Token überspringen
|
||||
|
||||
#debug("Starting namespace parse at token $i, T_NAMESPACE was at $originalI");
|
||||
|
||||
// Debug: Zeige die nächsten paar Tokens
|
||||
for ($debugI = $i; $debugI < min($i + 10, count($tokens)); $debugI++) {
|
||||
$token = $tokens[$debugI];
|
||||
}
|
||||
|
||||
while ($i < count($tokens)) {
|
||||
$token = $tokens[$i];
|
||||
|
||||
if (is_array($token)) {
|
||||
|
||||
if ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || $token[0] === T_NAME_QUALIFIED) {
|
||||
$namespace .= $token[1];
|
||||
} elseif ($token[0] === T_WHITESPACE) {
|
||||
// Whitespace ignorieren, aber weitermachen
|
||||
} else {
|
||||
#debug("Breaking on token: " . token_name($token[0]));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ($token === ';' || $token === '{') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
$result = trim($namespace, '\\');
|
||||
#debug("Final parsed namespace: '$result'");
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parst den Klassennamen aus den Tokens
|
||||
*/
|
||||
private static function parseClassName(array $tokens, int &$i): ?string
|
||||
{
|
||||
$i += 1; // T_CLASS/T_INTERFACE/T_TRAIT Token überspringen
|
||||
|
||||
// Suche nach dem Namen (nächstes T_STRING Token)
|
||||
while ($i < count($tokens)) {
|
||||
if ($tokens[$i][0] === T_STRING) {
|
||||
return $tokens[$i][1];
|
||||
} elseif ($tokens[$i] === '{' || $tokens[$i] === ';') {
|
||||
// Unerwartetes Ende ohne Klassennamen
|
||||
return null;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest Tokens aus einer Datei mit Caching
|
||||
*/
|
||||
private static function getTokens(string $file): ?array
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheKey = md5($file . filemtime($file));
|
||||
if (isset(self::$tokenCache[$cacheKey])) {
|
||||
return self::$tokenCache[$cacheKey];
|
||||
}
|
||||
|
||||
$code = file_get_contents($file);
|
||||
if ($code === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokens = token_get_all($code);
|
||||
self::$tokenCache[$cacheKey] = $tokens;
|
||||
|
||||
// Cache-Größe begrenzen
|
||||
if (count(self::$tokenCache) > 100) {
|
||||
// Entferne ältesten Eintrag
|
||||
array_shift(self::$tokenCache);
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiert Namespace aus Dateiinhalt als Fallback
|
||||
*/
|
||||
private static function extractNamespaceFromContent(string $file): string
|
||||
{
|
||||
$content = file_get_contents($file);
|
||||
if ($content === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Regex für namespace-Deklaration
|
||||
if (preg_match('/namespace\s+([^;]+);/', $content, $matches)) {
|
||||
return trim($matches[1]);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Leert den internen Cache
|
||||
*/
|
||||
public static function clearCache(): void
|
||||
{
|
||||
self::$classCache = [];
|
||||
self::$tokenCache = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extrahiert den Klassennamen (mit Namespace) aus einer PHP-Datei
|
||||
*/
|
||||
public function getClassNameFromFile(string $file): ?string
|
||||
{
|
||||
$contents = file_get_contents($file);
|
||||
if (
|
||||
preg_match('#namespace\s+([^;]+);#', $contents, $nsMatch)
|
||||
&& preg_match('/class\s+(\w+)/', $contents, $classMatch)
|
||||
) {
|
||||
return trim($nsMatch[1]) . '\\' . trim($classMatch[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user