openrat-cms

# OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

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