TemplateEngine.class.php (8099B)
1 <?php 2 3 4 namespace template_engine; 5 use DomainException; 6 use DOMDocument; 7 use DOMElement; 8 use Exception; 9 use LogicException; 10 use SimpleXMLElement; 11 use \template_engine\components\Component; 12 13 /** 14 * Wandelt eine Vorlage in ein PHP-Skript um. 15 * 16 * Die Vorlage wird gesparst, Elemente werden geladen und in die Zieldatei kopiert. 17 * 18 * @author Jan Dankert 19 * @package openrat.services 20 */ 21 class TemplateEngine 22 { 23 public $renderType = 'html'; 24 25 public $config = array(); 26 public $request; 27 28 private $srcFilename; 29 private $debug = false; 30 31 32 /** 33 * Erzeugt einen Templateparser. 34 */ 35 public function __construct() 36 { 37 } 38 39 /** 40 * Compile the template. 41 * From a XML source file we are generating a PHP file. 42 * 43 * @param $srcXmlFilename string Filename of template source 44 * @param $tplOutName string Filename of template code 45 */ 46 public function compile($srcXmlFilename, $tplOutName ) 47 { 48 $this->srcFilename = $srcXmlFilename; 49 50 // Imports the base class of all component types. 51 require_once (dirname(__FILE__).'/../components/'.$this->renderType.'/Component.class.' . PHP_EXT); 52 require_once (dirname(__FILE__).'/../components/'.$this->renderType.'/HtmlComponent.class.' . PHP_EXT); 53 require_once (dirname(__FILE__).'/../components/'.$this->renderType.'/FieldComponent.class.' . PHP_EXT); 54 55 try 56 { 57 $confCompiler = $this->config; 58 59 if (is_file($srcXmlFilename)) 60 $srcFilename = $srcXmlFilename; 61 else 62 // Wenn Vorlage (noch) nicht existiert 63 throw new LogicException("Template not found: $srcXmlFilename"); 64 65 $filename = $tplOutName; 66 67 // Wenn Vorlage gaendert wurde, dann Umwandlung erneut ausf�hren. 68 if (false && is_file($filename) && filemtime($srcFilename) <= filemtime($filename)) 69 return; 70 71 if (is_file($filename) && ! is_writable($filename)) 72 throw new LogicException("Template output file is read-only: $filename"); 73 74 // Vorlage und Zieldatei oeffnen 75 $document = $this->loadDocument($srcFilename); 76 77 $outFile = @fopen($filename, 'w'); 78 fwrite($outFile, '<?php if (!defined(\'OR_TITLE\')) die(\'Forbidden\'); ?>'); 79 80 if (! is_resource($outFile)) 81 throw new LogicException("Template '$srcXmlFilename': Unable to open file for writing: '$filename'"); 82 83 $this->processElement( $document->documentElement, $outFile ); 84 85 fclose($outFile); 86 87 // CHMOD ausfuehren. 88 if (! empty($confCompiler['chmod'])) 89 if (! @chmod($filename, octdec($confCompiler['chmod']))) 90 throw new \InvalidArgumentException("Template {$srcXmlFilename} failed to compile: CHMOD '{$confCompiler['chmod']}' failed on file {$filename}."); 91 } 92 catch (Exception $e) 93 { 94 echo $e->getTraceAsString(); 95 throw new LogicException("Template '$srcXmlFilename' failed to compile", 0, $e); 96 } 97 } 98 99 100 /** 101 * @param DOMElement $element 102 * @param resource $outFile 103 * @param int $depth 104 */ 105 private function processElement( $element, $outFile, $depth = 0 ) { 106 107 // Only process DOM Elements (ignoring Text, Comments, ...) 108 if ( $element->nodeType == XML_ELEMENT_NODE ) 109 ; 110 else 111 return; 112 113 // The namespace decides what to do with this element: 114 if ( $element->namespaceURI == 'http://www.openrat.de/template') 115 $this->processCMSElement( $element, $outFile, $depth ); 116 elseif ( $element->namespaceURI == 'http://www.w3.org/1999/xhtml') 117 $this->processHTMLElement( $element, $outFile, $depth ); 118 else 119 throw new LogicException("Unknown Element ".$element->tagName.' in NS '.$element->namespaceURI ); 120 } 121 122 123 124 private function processCMSElement(DOMElement $element, $outFile, $depth) 125 { 126 $attributes = $element->attributes; 127 $tag = $element->localName; 128 129 if ( $tag == 'include') { 130 131 $filename = dirname($this->srcFilename) . '/' . $attributes['file']->value . '.inc.xml'; 132 if ( ! is_file( $filename )) 133 throw new LogicException('Includefile not found: '.$filename ); 134 135 $element = $this->loadDocument($filename)->documentElement; 136 $attributes = $element->attributes; 137 $tag = $element->localName; 138 } 139 140 $className = ucfirst($tag); 141 $classFilename = dirname(__FILE__).'/../components/'.$this->renderType."/$tag/$className.class." . PHP_EXT; 142 143 if (!is_file($classFilename)) 144 throw new LogicException("Component Class File '$classFilename' does not exist." ); 145 146 require_once ($classFilename); 147 148 $className = 'template_engine\components\\'.$className .'Component'; 149 /* @var $component Component */ 150 $component = new $className(); 151 $component->setDepth($depth+1); 152 $component->request = $this->request; 153 154 foreach ($attributes as $attribute) 155 { 156 $attributeValue = $attribute->value; 157 $attributeName = $attribute->name; 158 159 // Aus String 'true' und 'false' typechtes Boolean machen. 160 // Sonst wäre 'false'==true! 161 if ($attributeValue == 'false') $attributeValue = false; 162 if ($attributeValue == 'true') $attributeValue = true; 163 164 $component->$attributeName = $attributeValue; 165 } 166 167 $output = $component->getBegin(); 168 if($this->debug) $output = '<!-- Begin '.$tag.' -->'.$output; 169 170 if ($output) { 171 $prepend = ($depth>=0?"\n":'').str_repeat("\t",$depth); 172 fwrite($outFile, $prepend.$output); 173 } 174 175 foreach( $element->childNodes as $child ) { 176 $this->processElement($child,$outFile,$depth+1); 177 } 178 179 $output = $component->getEnd(); 180 if($this->debug) $output = $output.'<!-- End '.$tag.' -->'; 181 if ( $output ) { 182 $prepend = "\n".str_repeat("\t",$depth); 183 fwrite($outFile, $prepend.$output); 184 } 185 186 } 187 188 189 190 private function processHTMLElement(DOMElement $element, $outFile, $depth) 191 { 192 $attributes = $element->attributes; 193 $tag = $element->localName; 194 195 $out = '<'.$tag; 196 foreach ($attributes as $attribute) 197 $out .= ' '.$attribute->name.'="'.$attribute->value.'"'; 198 199 $out .= '>'; 200 201 $prepend = ($depth>=0?"\n":'').str_repeat("\t",$depth); 202 fwrite($outFile, $prepend.$out); 203 204 foreach( $element->childNodes as $child ) { 205 $this->processElement($child,$outFile,$depth+1); 206 } 207 208 $out = '</'.$tag.'>'; 209 $prepend = "\n".str_repeat("\t",$depth); 210 fwrite($outFile, $prepend.$out); 211 212 } 213 214 /** 215 * Diese Funktion lädt die Vorlagedatei. 216 */ 217 private function loadDocument( $filename ) 218 { 219 return $this->loadXmlDocument( $filename ); 220 } 221 222 223 /** 224 * Laden und Parsen eines XML-Dokumentes. 225 * 226 * @return DOMDocument 227 */ 228 private function loadXmlDocument( $filename ) 229 { 230 if (!is_file($filename)) 231 throw new LogicException("XML file '$filename' was not found.'"); 232 233 $document = new DOMDocument(); 234 $document->load( $filename ); 235 return $document; 236 } 237 238 239 /** 240 * Führt das gewünschte Template aus und schreibt das Ergebnis auf die Standardausgabe. 241 * 242 * In Development-Mode the template is compiled. 243 * 244 * @param $srcFile string Quelldateiname des Templates (im XML-Format) 245 * @param $outputData array Ausgabedaten 246 */ 247 public function executeTemplate($srcFile, $outputData) 248 { 249 // Converting filename: '/path/file.src.xml' => '/path/file.php'. 250 $templateFile = dirname( $srcFile ).'/'.substr( basename($srcFile),0,strpos( basename($srcFile),'.')).'.php'; 251 252 // In development mode, we are compiling every template on the fly. 253 if (DEVELOPMENT && false /*use dedicated template compiler in update.html */) { 254 255 // Compile the template. 256 // From a XML source file we are generating a PHP file. 257 try 258 { 259 $this->compile($srcFile, $templateFile); 260 unset($te); 261 } catch (Exception $e) { 262 throw new DomainException("Compilation failed for Template '$srcFile'.", 0, $e); 263 } 264 #header("X-CMS-Template-File: " . $templateFile); 265 } 266 267 // Spätestens jetzt muss das Template vorhanden sein. 268 if (!is_file($templateFile)) 269 throw new LogicException("Template file '$templateFile' was not found."); 270 271 // Übertragen der Ausgabe-Variablen in den aktuellen Kontext 272 // 273 extract($outputData); 274 275 // Einbinden des Templates 276 require_once($templateFile); 277 278 } 279 280 } 281