File modules/template_engine/engine/TemplateEngine.class.php

Last commit: Wed Nov 17 23:22:19 2021 +0100	Jan Dankert	Refactoring: Using a template context for templates instead of the HTTP-Request-Data
1 <?php 2 3 4 namespace template_engine\engine; 5 use DomainException; 6 use DOMDocument; 7 use DOMElement; 8 use Exception; 9 use LogicException; 10 use template_engine\element\XMLFormatter; 11 use template_engine\element\PHPBlockElement; 12 use template_engine\components\html\Component; 13 use template_engine\components\html\NativeHtmlComponent; 14 15 /** 16 * Converting an XML-Template to a PHP file. 17 * 18 * @author Jan Dankert 19 * @package openrat.services 20 */ 21 class TemplateEngine 22 { 23 /** 24 * The OpenRat template namespace. 25 */ 26 const NS_OPENRAT_COMPONENT = 'http://www.openrat.de/template'; 27 28 /** 29 * HTML5 namespace. 30 */ 31 const NS_HTML5 = 'http://www.w3.org/1999/xhtml'; 32 33 // For now we are only supporting HTML rendering. 34 public $renderType = 'html'; 35 36 public $config = array(); 37 public $context; 38 39 private $srcFilename; 40 41 const CSS_PREFIX = 'or-'; 42 43 const OUTPUT_ALIAS = 'O'; 44 45 /** 46 * Compile the template. 47 * From a XML source file we are generating a PHP file. 48 * 49 * @param $srcXmlFilename string Filename of template source 50 * @param $tplOutName string Filename of template code 51 */ 52 public function compile($srcXmlFilename, $tplOutName ) 53 { 54 $this->srcFilename = $srcXmlFilename; 55 56 $filename = $tplOutName; 57 58 if (is_file($filename) && ! is_writable($filename)) 59 throw new LogicException("Template output file is read-only: $filename"); 60 61 // The generated template should only be executable in our CMS environment (for security reasons). 62 63 $initCommands = [ 64 '/* THIS FILE IS GENERATED from '.basename($srcXmlFilename).' - DO NOT CHANGE */', 65 'defined(\'APP_STARTED\') || die(\'Forbidden\');', 66 'use \\template_engine\Output as '.self::OUTPUT_ALIAS.';' 67 ]; 68 $writtenBytes = file_put_contents( $filename, '<?php '.implode(' ',$initCommands).' ?>' ); 69 70 if ( $writtenBytes === FALSE ) 71 throw new LogicException("Unable writing to output file: '$filename'"); 72 73 try 74 { 75 $confCompiler = $this->config; 76 77 if (is_file($srcXmlFilename)) 78 $srcFilename = $srcXmlFilename; 79 else 80 // Wenn Vorlage (noch) nicht existiert 81 throw new LogicException("Template not found: $srcXmlFilename"); 82 83 // Vorlage und Zieldatei oeffnen 84 $document = $this->loadDocument($srcFilename); 85 86 // creating a tree of components 87 $rootComponent = $this->processElement( $document->documentElement ); 88 89 if ( $document->doctype ) { 90 file_put_contents( $filename, '<!DOCTYPE '.$document->doctype->name.'>',FILE_APPEND ); 91 } 92 93 // converting the component tree to a element tree 94 $rootElement = $rootComponent->getElement(); 95 96 // Rendering the complete element tree into a file. 97 file_put_contents( $filename, $rootElement->render( new XMLFormatter(' ')),FILE_APPEND ); 98 99 // CHMOD ausfuehren. 100 if ( @$confCompiler['chmod'] ) 101 if (! @chmod($filename, octdec($confCompiler['chmod']))) 102 throw new \InvalidArgumentException("CHMOD '{$confCompiler['chmod']}' failed on file {$filename}."); 103 } 104 catch (Exception $e) 105 { 106 throw new LogicException("Template '$srcXmlFilename' failed to compile", 0, $e); 107 } 108 } 109 110 111 /** 112 * @param DOMElement $element 113 * @param int $depth 114 */ 115 private function processElement( $element, $depth = 0 ) { 116 117 // Only process DOM Elements (ignoring Text, Comments, ...) 118 if ( $element->nodeType == XML_ELEMENT_NODE ) 119 ; 120 else 121 return null; 122 123 // The namespace decides what to do with this element: 124 if ( $element->namespaceURI == self::NS_OPENRAT_COMPONENT) 125 return $this->processCMSElement( $element, $depth ); 126 elseif ( $element->namespaceURI == self::NS_HTML5) 127 return $this->processHTMLElement( $element, $depth ); 128 else 129 throw new LogicException("Unknown Element ".$element->tagName.' in NS '.$element->namespaceURI ); 130 } 131 132 133 134 private function processCMSElement(DOMElement $element, $depth) 135 { 136 $attributes = $element->attributes; 137 $tag = $element->localName; 138 139 if ( $tag == 'include') { 140 141 $filename = dirname($this->srcFilename) . '/' . $attributes['file']->value . '.inc.xml'; 142 if ( ! is_file( $filename )) 143 throw new LogicException('Includefile not found: '.$filename ); 144 145 $element = $this->loadDocument($filename)->documentElement; 146 $attributes = $element->attributes; 147 $tag = $element->localName; 148 } 149 150 $className = ucfirst($tag); 151 $className = 'template_engine\\components\\html\\component_'.$tag.'\\'.$className.'Component'; 152 153 if ( !class_exists($className )) 154 throw new LogicException("Component class ".$className.' does not exist'); 155 156 /* @var $component Component */ 157 $component = new $className(); 158 $component->setDepth($depth+1); 159 $component->context = $this->context; 160 161 foreach ($attributes as $attribute) 162 { 163 $attributeValue = $attribute->value; 164 $attributeName = $attribute->name; 165 166 // Aus String 'true' und 'false' typechtes Boolean machen. 167 // Sonst wäre 'false'==true! 168 if ($attributeValue == 'false') $attributeValue = false; 169 if ($attributeValue == 'true') $attributeValue = true; 170 171 $component->$attributeName = $attributeValue; 172 } 173 174 $component->init(); 175 176 foreach( $element->childNodes as $child ) { 177 $component->addChildComponent( $this->processElement( $child,$depth+1 ) ); 178 } 179 return $component; 180 } 181 182 183 /** 184 * Creates a new HTML element. 185 * @param $element DOMElement 186 * @param $depth 187 * @return NativeHtmlComponent 188 */ 189 private function processHTMLElement($element, $depth) 190 { 191 $component = new NativeHtmlComponent(); 192 193 $component->tag = $element->localName; 194 195 $element->removeAttribute('xsi:schemaLocation'); 196 $component->attributes = $element->attributes; 197 $component->init(); 198 199 foreach( $element->childNodes as $child ) { 200 $component->addChildComponent( $this->processElement( $child,$depth+1 ) ); 201 } 202 203 return $component; 204 } 205 206 /** 207 * Diese Funktion lädt die Vorlagedatei. 208 */ 209 private function loadDocument( $filename ) 210 { 211 return $this->loadXmlDocument( $filename ); 212 } 213 214 215 /** 216 * Laden und Parsen eines XML-Dokumentes. 217 * 218 * @return DOMDocument 219 */ 220 private function loadXmlDocument( $filename ) 221 { 222 if (!is_file($filename)) 223 throw new LogicException("XML file '$filename' was not found.'"); 224 225 $document = new DOMDocument(); 226 $document->load( $filename ); 227 return $document; 228 } 229 } 230
Download modules/template_engine/engine/TemplateEngine.class.php
History Wed, 17 Nov 2021 23:22:19 +0100 Jan Dankert Refactoring: Using a template context for templates instead of the HTTP-Request-Data Sun, 15 Nov 2020 21:52:19 +0100 Jan Dankert Optimized template element renderer: Elements without children are closed in the same line. This fixes the space problem in HTML textareas. Sat, 31 Oct 2020 00:43:29 +0100 Jan Dankert New: Support for OpenId Connect; Removed: Support for LDAP. Thu, 22 Oct 2020 13:57:19 +0200 Jan Dankert Cleanup: initCommands as array. Thu, 22 Oct 2020 13:54:16 +0200 Jan Dankert A notice for generated files. Thu, 22 Oct 2020 13:49:13 +0200 Jan Dankert Refactoring: Generating the root HTML with a clean template. Wed, 14 Oct 2020 22:36:29 +0200 Jan Dankert Refactoring: Fixed the namespace in component classes, now the are able to be load by the standard autoloader. Mon, 5 Oct 2020 23:48:34 +0200 Jan Dankert Fix: HTML-comments are destroying binary output. Sat, 3 Oct 2020 00:50:14 +0200 Jan Dankert Fix: Show the rights of node objects. Sun, 27 Sep 2020 04:09:05 +0200 Jan Dankert Refactoring: The tree functions should use normal templates as the other actions. Beware of the JS hell. Sat, 26 Sep 2020 22:07:53 +0200 Jan Dankert Removing superfluous code. Sat, 26 Sep 2020 21:05:03 +0200 Jan Dankert Refactoring: An alias for the Output class, this results in a cleaner template. Sat, 26 Sep 2020 13:11:23 +0200 Jan Dankert Refactoring: No global variables any more. All constants are capsulated by classes. Sat, 26 Sep 2020 12:20:43 +0200 Jan Dankert Refactoring: No global variables like $SESS any more. All constants are capsulated by classes. Sat, 29 Aug 2020 03:23:06 +0200 Jan Dankert Refactoring: Improved Exception-Handling; New: Generating pages using a page context which considers page aliases. Sun, 23 Feb 2020 04:55:35 +0100 Jan Dankert New: Formatter for indenting XML files. Sat, 22 Feb 2020 22:23:01 +0100 Jan Dankert Refactoring: Enable Autoloading, Fix namespace structure. Sat, 22 Feb 2020 01:58:20 +0100 Jan Dankert New: Autoloading of classes in all modules. Sat, 22 Feb 2020 00:46:59 +0100 Jan Dankert Refactoring: Renaming template-engine to template_engine because '-' is no valid namespace char.