viewPath = __DIR__ . '/../Modules'; } /** * Render a view */ public function render(string $view, array $data = []): string { $data = array_merge($this->sharedData, $data); // Get view file path $viewFile = $this->getViewFile($view); if (!file_exists($viewFile)) { throw new \Exception("View '{$view}' not found"); } // Read view file content $content = file_get_contents($viewFile); // Process template syntax first $content = $this->processTemplate($content); // Debug: Log processed content if (getenv('APP_DEBUG') === 'true') { error_log("Processed template content: " . substr($content, 0, 500)); } // Extract data to variables extract($data); // Start output buffering ob_start(); // Evaluate processed content try { eval('?>' . $content); } catch (ParseError $e) { // Log the problematic content for debugging error_log("Template parse error: " . $e->getMessage()); error_log("Problematic content around line " . $e->getLine() . ": " . substr($content, max(0, $e->getLine() - 10) * 50, 1000)); throw $e; } // Get content and clean buffer return ob_get_clean(); } /** * Get view file path */ private function getViewFile(string $view): string { // Convert dot notation to path $view = str_replace('.', '/', $view); // Add .php extension if not present if (!str_ends_with($view, '.php')) { $view .= '.php'; } return $this->viewPath . '/' . $view; } /** * Process template syntax */ private function processTemplate(string $content): string { // Process {{ }} syntax (auto-escape) $content = preg_replace_callback('/\{\{\s*(.+?)\s*\}\}/', function ($matches) { $expression = trim($matches[1]); return ''; }, $content); // Process {!! !!} syntax (raw output) $content = preg_replace_callback('/\{!!\s*(.+?)\s*!!\}/', function ($matches) { $expression = trim($matches[1]); return ''; }, $content); // Process @if statements $content = preg_replace('/@if\s*\(\s*([^)]+)\s*\)/', '', $content); $content = preg_replace('/@elseif\s*\(\s*([^)]+)\s*\)/', '', $content); $content = preg_replace('/@else/', '', $content); $content = preg_replace('/@endif/', '', $content); // Debug: Log processed @if statements if (getenv('APP_DEBUG') === 'true') { error_log("After @if processing: " . substr($content, 0, 1000)); } // Process @foreach loops $content = preg_replace('/@foreach\s*\((.+?)\)/', '', $content); $content = preg_replace('/@endforeach/', '', $content); // Process @for loops $content = preg_replace('/@for\s*\((.+?)\)/', '', $content); $content = preg_replace('/@endfor/', '', $content); // Process @while loops $content = preg_replace('/@while\s*\((.+?)\)/', '', $content); $content = preg_replace('/@endwhile/', '', $content); // Process @csrf directive $content = preg_replace('/@csrf/', '', $content); // Process @method directive $content = preg_replace('/@method\s*\((.+?)\)/', '', $content); return $content; } /** * Share data with all views */ public function share(string $key, $value): void { $this->sharedData[$key] = $value; } /** * Check if view exists */ public function exists(string $view): bool { $viewFile = $this->getViewFile($view); return file_exists($viewFile); } /** * Get view path */ public function getViewPath(): string { return $this->viewPath; } /** * Set view path */ public function setViewPath(string $path): void { $this->viewPath = $path; } }