diff --git a/README.md b/README.md index 3135a2e..8ebcd3e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ __DIR__ . '/templates', diff --git a/autoload.php b/autoload.php index 7f0c689..9890e94 100644 --- a/autoload.php +++ b/autoload.php @@ -6,17 +6,30 @@ */ spl_autoload_register(function ($class) { - $prefix = 'Beobles\\Core\\View\\'; + $prefixes = ['Core\\View\\', 'Beobles\\Core\\View\\']; $baseDir = __DIR__ . '/src/Core/View/'; - if (strpos($class, $prefix) !== 0) { - return; - } + foreach ($prefixes as $prefix) { + if (strpos($class, $prefix) !== 0) { + continue; + } + + $relativeClass = substr($class, strlen($prefix)); + $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php'; - $relativeClass = substr($class, strlen($prefix)); - $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php'; + if (!file_exists($file)) { + return; + } - if (file_exists($file)) { require $file; + + if ($prefix === 'Beobles\\Core\\View\\') { + $newClass = 'Core\\View\\' . $relativeClass; + if (class_exists($newClass, false) && !class_exists($class, false)) { + class_alias($newClass, $class); + } + } + + return; } }); diff --git a/examples/index.php b/examples/index.php index 39253a0..9392a5e 100644 --- a/examples/index.php +++ b/examples/index.php @@ -2,7 +2,7 @@ require_once __DIR__ . '/../autoload.php'; -use Beobles\Core\View\Engine; +use Core\View\Engine; // Criar engine $engine = new Engine([ diff --git a/src/Core/View/Abstract/AbstractDirective.php b/src/Core/View/Abstract/AbstractDirective.php index 6bde27f..529c943 100644 --- a/src/Core/View/Abstract/AbstractDirective.php +++ b/src/Core/View/Abstract/AbstractDirective.php @@ -1,6 +1,6 @@ resolveValue(' . var_export($base, true) . ', get_defined_vars())'; - } else { - $compiled = $this->transformDotNotation($base); - } + $compiled = $this->transformExpression($base); foreach ($parts as $part) { $part = trim($part); @@ -164,15 +159,154 @@ private function compileExpression(string $expression): string return $compiled; } - private function transformDotNotation(string $expression): string + private function transformExpression(string $expression): string + { + $expression = trim($expression); + if ($expression === '') { + return 'null'; + } + + $expression = $this->transformMemberNotation($expression); + return $this->prefixBareIdentifiers($expression); + } + + private function transformMemberNotation(string $expression): string { + $expression = preg_replace( + '/\b([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\s*\(/', + '\$$1->$2(', + $expression + ) ?? $expression; + return preg_replace_callback( - '/\b([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+)\b/', - fn(array $matches): string => '$__engine->resolveValue(' . var_export($matches[1], true) . ', get_defined_vars())', + '/\b(\$?[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)+)\b/', + static function (array $matches): string { + $path = $matches[1]; + $segments = explode('.', ltrim($path, '$')); + $result = '$' . array_shift($segments); + + foreach ($segments as $segment) { + $result .= "['{$segment}']"; + } + + return $result; + }, $expression ) ?? $expression; } + private function prefixBareIdentifiers(string $expression): string + { + $tokens = token_get_all('shouldPrefixIdentifier($text, $tokens, $i)) { + $compiled .= $text; + continue; + } + + $compiled .= '$' . $text; + } + + return trim($compiled); + } + + /** @param array $tokens */ + private function shouldPrefixIdentifier(string $text, array $tokens, int $index): bool + { + $lower = strtolower($text); + if (in_array($lower, [ + 'true', + 'false', + 'null', + 'and', + 'or', + 'xor', + 'instanceof', + 'new', + 'clone', + 'default', + 'match', + 'fn', + 'self', + 'static', + 'parent', + ], true)) { + return false; + } + + if (preg_match('/^[A-Z_][A-Z0-9_]*$/', $text) === 1) { + return false; + } + + $prev = $this->previousSignificantToken($tokens, $index); + $next = $this->nextSignificantToken($tokens, $index); + + if (($prev['text'] ?? null) === '->' || ($prev['text'] ?? null) === '::' || ($prev['id'] ?? null) === T_VARIABLE) { + return false; + } + + if (($next['text'] ?? null) === '(') { + return false; + } + + return true; + } + + /** @param array $tokens + * @return array{id:int|null,text:string|null} + */ + private function previousSignificantToken(array $tokens, int $index): array + { + for ($i = $index - 1; $i >= 0; $i--) { + $token = $tokens[$i]; + if (is_string($token)) { + if (trim($token) !== '') { + return ['id' => null, 'text' => $token]; + } + continue; + } + + if ($token[0] !== T_WHITESPACE) { + return ['id' => $token[0], 'text' => $token[1]]; + } + } + + return ['id' => null, 'text' => null]; + } + + /** @param array $tokens + * @return array{id:int|null,text:string|null} + */ + private function nextSignificantToken(array $tokens, int $index): array + { + $count = count($tokens); + for ($i = $index + 1; $i < $count; $i++) { + $token = $tokens[$i]; + if (is_string($token)) { + if (trim($token) !== '') { + return ['id' => null, 'text' => $token]; + } + continue; + } + + if ($token[0] !== T_WHITESPACE) { + return ['id' => $token[0], 'text' => $token[1]]; + } + } + + return ['id' => null, 'text' => null]; + } + /** @param array $nodes */ private function compileChildren(array $nodes): string { diff --git a/src/Core/View/Components/ComponentRegistry.php b/src/Core/View/Components/ComponentRegistry.php index f7a1e70..30b8135 100644 --- a/src/Core/View/Components/ComponentRegistry.php +++ b/src/Core/View/Components/ComponentRegistry.php @@ -1,8 +1,8 @@