openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

TemplateEngine.class.php (6201B)


      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