viewsPath = rtrim($viewsPath, '/') . '/'; } /** * Setzt globale Variablen für alle Templates. * * @param array $globals Assoziatives Array mit globalen Variablen. */ public function setGlobals(array $globals) { $this->globals = $globals; } /** * Rendert ein Template mit den übergebenen Daten. * * @param string $file Name der Template-Datei (ohne Pfad). * @param array $data Assoziatives Array mit Variablen für das Template. * @return string Gerenderter HTML-Code. * @throws Exception Wenn das Template nicht gefunden oder ein Fehler auftritt. */ public function render($file, $data = []): string { $data = array_merge($this->globals, $data); try { $code = $this->includeFiles($file); $code = $this->compileCode($code); extract($data, EXTR_SKIP); ob_start(); eval('?>' . $code); return ob_get_clean(); } catch(Exception $e) { throw new Exception("Error rendering template: " . $e->getMessage()); } } /** * Wandelt den Template-Code in ausführbaren PHP-Code um. * * @param string $code Der Template-Code. * @return string Kompilierter PHP-Code. */ private function compileCode($code): string { $code = $this->compileBlock($code); $code = $this->compileYield($code); $code = $this->compileEchos($code); $code = $this->compilePHP($code); return $code; } /** * Fügt eingebundene oder erweiterte Templates ein. * * @param string $file Name der Template-Datei. * @return string Template-Code mit eingebundenen Dateien. * @throws Exception Wenn die Datei nicht gefunden wird. */ private function includeFiles($file): string { $filePath = $this->viewsPath . preg_replace("/\.twig$/", "", $file) . ".twig"; if(!file_exists($filePath)) { throw new Exception("View file not found: {$filePath}"); } $code = file_get_contents($filePath); preg_match_all('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', $code, $matches, PREG_SET_ORDER); foreach($matches as $match) { $includedCode = $this->includeFiles($match[2]); $code = str_replace($match[0], $includedCode, $code); } return preg_replace('/{% ?(extends|include) ?\'?(.*?)\'? ?%}/i', '', $code); } /** * Wandelt {% ... %} in PHP-Code um. * * @param string $code Der Template-Code. * @return string PHP-Code. */ private function compilePHP($code): string { return preg_replace('~\{%\s*(.+?)\s*%}~is', '', $code); } /** * Wandelt {{ ... }} in PHP-Echo-Ausgaben um. * * @param string $code Der Template-Code. * @return string PHP-Code mit Echo-Ausgaben. */ private function compileEchos($code): string { $code = preg_replace('~\{\{\{\s*(.+?)\s*\}\}\}~is', '', $code); return preg_replace('~\{\{\s*(.+?)\s*\}\}~is', '', $code); } /** * Sammelt und entfernt Block-Definitionen aus dem Template. * * @param string $code Der Template-Code. * @return string Template-Code ohne Block-Definitionen. */ private function compileBlock($code): string { preg_match_all('/{% ?block ?(.*?) ?%}(.*?){% ?endblock ?%}/is', $code, $matches, PREG_SET_ORDER); foreach($matches as $match) { if(!isset($this->blocks[$match[1]])) { $this->blocks[$match[1]] = ''; } $this->blocks[$match[1]] = str_replace('@parent', $this->blocks[$match[1]], $match[2]); $code = str_replace($match[0], '', $code); } return $code; } /** * Ersetzt yield-Platzhalter durch Block-Inhalte. * * @param string $code Der Template-Code. * @return string Template-Code mit ersetzten Yields. */ private function compileYield($code): string { foreach($this->blocks as $block => $value) { $code = preg_replace('/{% ?yield ?' . $block . ' ?%}/', $value, $code); } return preg_replace('/{% ?yield ?(.*?) ?%}/i', '', $code); } }