PageRenderTime 48ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/core/template.class.php

https://github.com/konfirm/konsolidate
PHP | 383 lines | 164 code | 32 blank | 187 comment | 19 complexity | b3a3953228b2a2506ca0a7f0069aa6cd MD5 | raw file
  1. <?php
  2. /*
  3. * ________ ___
  4. * / / /\ /\ Konsolidate
  5. * ____/ /___/ \/ \
  6. * / /\ / http://www.konsolidate.nl
  7. * /___ ___/ \ /
  8. * \ / /\ \ / \ Class: CoreTemplate
  9. * \/___/ \___\/ \ Tier: Core
  10. * \ \ /\ \ /\ / Module: Template
  11. * \___\/ \___\/ \/
  12. * \ \ / $Rev$
  13. * \___ ___\/ $Author$
  14. * \ \ / $Date$
  15. * \___\/
  16. */
  17. /**
  18. * Two step template engine (compilation and runtime parsing, where compilations are stored as cache)
  19. * @name CoreTemplate
  20. * @type class
  21. * @package Konsolidate
  22. * @author Rogier Spieker <rogier@konsolidate.nl>
  23. */
  24. class CoreTemplate extends Konsolidate
  25. {
  26. /**
  27. * Where to look for templates
  28. * @name _templatepath
  29. * @type variable
  30. * @access protected
  31. */
  32. protected $_templatepath;
  33. /**
  34. * Where to store the compiled templates
  35. * @name _compilepath
  36. * @type variable
  37. * @access protected
  38. */
  39. protected $_compilepath;
  40. /**
  41. * How long ago was the document compiled
  42. * @name _compiletime
  43. * @type variable
  44. * @access protected
  45. */
  46. protected $_compiletime;
  47. /**
  48. * Does the server support short PHP open tags
  49. * @name _shortopentag
  50. * @type boolean
  51. * @access protected
  52. */
  53. protected $_shortopentag;
  54. /**
  55. * constructor
  56. * @name __construct
  57. * @type constructor
  58. * @access public
  59. * @param object parent object
  60. * @return object
  61. * @syntax object = &new CoreTemplate( object parent )
  62. * @note This object is constructed by one of Konsolidates modules
  63. */
  64. public function __construct( $oParent )
  65. {
  66. parent::__construct( $oParent );
  67. $this->_templatepath = $this->get( "/Config/Path/template", realpath( ( defined( "TEMPLATE_PATH" ) ? TEMPLATE_PATH : "./templates" ) ) );
  68. $this->_compilepath = $this->get( "/Config/Path/compile", realpath( ( defined( "COMPILE_PATH" ) ? COMPILE_PATH : "./compile" ) ) );
  69. $this->_shortopentag = (bool) ini_get( "short_open_tag" );
  70. ini_set( "include_path", $this->_templatepath . PATH_SEPARATOR . ini_get( "include_path" ) ); }
  71. /**
  72. * Fetch the built template output
  73. * @name fetch
  74. * @type method
  75. * @access public
  76. * @param string template
  77. * @param string reference [optional]
  78. * @param bool force [optional]
  79. * @return string document
  80. * @syntax Object->fetch( string template [, string reference [, bool force ] ] );
  81. */
  82. public function fetch( $sTemplate, $sReference="", $bForce=false )
  83. {
  84. return $this->_compose( $sTemplate, $sReference, $bForce );
  85. }
  86. /**
  87. * Dumps the built template output to browser
  88. * @name display
  89. * @type method
  90. * @access public
  91. * @param string template
  92. * @param string reference [optional]
  93. * @param bool force [optional]
  94. * @return bool success
  95. * @syntax Object->display( string template [, string reference [, bool force ] ] );
  96. */
  97. public function display( $sTemplate, $sReference="", $bForce=false )
  98. {
  99. return print( $this->fetch( $sTemplate, $sReference, $bForce ) );
  100. }
  101. /**
  102. * Append a variable to the template engine in order to make it known inside the template
  103. * @name append
  104. * @type method
  105. * @access public
  106. * @param mixed either a variable name or an array with name=>value pairs
  107. * @param mixed the value to set, ignored if 'variable' is an array [optional]
  108. * @return void
  109. * @syntax Object->append( mixed variable [, mixed value ] );
  110. */
  111. public function append( $mVariable, $mValue=null )
  112. {
  113. if ( is_array( $mVariable ) )
  114. {
  115. foreach( $mVariable as $sVariable=>$mValue )
  116. $this->append( $sVariable, $mValue );
  117. }
  118. else
  119. {
  120. $mCurrent = $this->$mVariable;
  121. if ( !is_null( $mCurrent ) )
  122. {
  123. switch( getType( $mCurrent ) )
  124. {
  125. case "boolean":
  126. $mCurrent &= $mValue;
  127. break;
  128. case "integer":
  129. case "double": // for historical reasons "double" is returned in case of a float, and not simply "float"
  130. $mCurrent += $mValue;
  131. break;
  132. case "string":
  133. $mCurrent .= $mValue;
  134. break;
  135. case "array":
  136. if ( is_array( $mValue ) )
  137. foreach( $mValue as $sKey=>$mVal )
  138. if ( is_int( $sKey ) )
  139. array_push( $mCurrent, $mVal );
  140. else
  141. $mCurrent[ $sKey ] = $mVal;
  142. else
  143. array_push( $mCurrent, $mValue );
  144. break;
  145. case "object":
  146. case "resource":
  147. case "NULL":
  148. case "user function": // depricated since PHP 4
  149. case "unknown type": // omgwtfbbq!1one!
  150. default:
  151. $this->$mVariable = $mValue;
  152. break;
  153. }
  154. $this->$mVariable = $mCurrent;
  155. }
  156. else
  157. {
  158. $this->$mVariable = $mValue;
  159. }
  160. }
  161. return true;
  162. }
  163. /**
  164. * Aet a variable to the template engine in order to make it known inside the template
  165. * @name set
  166. * @type method
  167. * @access public
  168. * @param mixed either a variable name or an array with name=>value pairs
  169. * @param mixed the value to set, ignored if 'variable' is an array [optional]
  170. * @param bool should the variable overwrite or extend (append) existing values?
  171. * @return void
  172. * @syntax Object->set( mixed variable [, mixed value [, bool append ] ] );
  173. */
  174. public function set()
  175. {
  176. // in order to achieve compatiblity with Konsolidates set method in strict mode, the params are read 'manually'
  177. $aParam = func_get_args();
  178. $mVariable = array_shift( $aParam );
  179. $mValue = (bool) count( $aParam ) ? array_shift( $aParam ) : null;
  180. $bAppend = (bool) count( $aParam ) ? array_shift( $aParam ) : false;
  181. if ( $bAppend === true )
  182. $this->append( $mVariable, $mValue );
  183. else
  184. parent::set( $mVariable, $mValue );
  185. }
  186. /**
  187. * Checks whether one (or more) of the dependencies has been updated after the precompilation cache has been built
  188. * @name isUpdated
  189. * @type method
  190. * @access public
  191. * @param string template
  192. * @param string reference [optional]
  193. * @return bool updated
  194. * @syntax Object->isUpdated( string template [, string reference ] );
  195. */
  196. public function isUpdated( $sTemplate, $sReference="" )
  197. {
  198. $sCacheFile = $this->_getCompileName( $sTemplate, $sReference );
  199. $nLastCompile = $this->_getCompileUpdateTime( $sCacheFile );
  200. if ( file_exists( "{$this->_compilepath}/{$sCacheFile}" ) )
  201. if ( $nLastCompile > $this->_getDependencyUpdateTime( $sCacheFile ) )
  202. return false;
  203. return true;
  204. }
  205. /**
  206. * Reversed implementation of isUpdated
  207. * @name isCompiled
  208. * @type method
  209. * @access public
  210. * @param string template
  211. * @param string reference [optional]
  212. * @return bool compiled
  213. * @syntax Object->isCompiled( string template [, string reference ] );
  214. * @see isUpdated
  215. * @note This alias method exists to make switching from CoreTemplate to NiceTemplate (and vice versa) painless
  216. */
  217. public function isCompiled( $sTemplate, $sReference="" )
  218. {
  219. return !$this->isUpdated( $sTemplate, $sReference );
  220. }
  221. /**
  222. * Builds the template
  223. * @name _compose
  224. * @type method
  225. * @access protected
  226. * @param string template
  227. * @param string reference [optional]
  228. * @param bool force [optional]
  229. * @return string document
  230. * @syntax Object->_compose( string template [, string reference [, bool force ] ] );
  231. */
  232. protected function _compose( $sTemplate, $sReference="", $bForce=false )
  233. {
  234. $sCacheFile = $this->_getCompileName( $sTemplate, $sReference );
  235. // prepare variables in the current scope
  236. foreach( $this->_property as $sVariable=>$sValue )
  237. $$sVariable = $sValue;
  238. if ( $bForce === true || $this->isUpdated( $sTemplate, $sReference ) )
  239. {
  240. // start capturing the output
  241. ob_start();
  242. // include the template, so the PHP code inside is executed and the content is send to the output buffer
  243. if ( $sTemplate{0} != "/" )
  244. $sTemplate = "{$this->_templatepath}/{$sTemplate}";
  245. if ( !file_exists( $sTemplate ) )
  246. throw new Exception( "Template not found '$sTemplate'" );
  247. include( $sTemplate );
  248. // get the buffer contents and convert the request-time PHP tags to normal PHP tags
  249. $sCapture = strtr( ob_get_contents(), Array( "<!?"=>"<?", "?!>"=>"?>" ) );
  250. // end and clean the output buffer
  251. ob_end_clean();
  252. if ( !$this->_shortopentag )
  253. {
  254. // The captured output may require a bit of rewriting
  255. $sCapture = preg_replace( "/<\?=\s*/", "<?php echo ", $sCapture );
  256. $sCapture = preg_replace( "/<\?[^php]/", "<?php", $sCapture );
  257. }
  258. if ( !$this->_storeCompilation( $sCacheFile, $sCapture ) )
  259. $this->call( "/Log/write", "Store of compilation has failed for template {$sTemplate} in file {$sCacheFile}" );
  260. }
  261. ob_start();
  262. include( "{$this->_compilepath}/{$sCacheFile}" );
  263. $sCapture = ob_get_contents();
  264. ob_end_clean();
  265. return $sCapture;
  266. }
  267. /**
  268. * Prepare paths and save compiled data to the filesystem
  269. * @name _storeCompilation
  270. * @type method
  271. * @access protected
  272. * @param string filename
  273. * @param string content
  274. * @return void
  275. * @syntax Object->_storeCompilation( string cachefile, string content );
  276. */
  277. protected function _storeCompilation( $sCacheFile, $sSource )
  278. {
  279. if ( !is_dir( "{$this->_compilepath}/dep/" ) )
  280. mkdir( "{$this->_compilepath}/dep/" );
  281. return (
  282. $this->_storeData( "{$this->_compilepath}/dep/" . md5( $sCacheFile ), serialize( get_included_files() ) ) &&
  283. $this->_storeData( "{$this->_compilepath}/{$sCacheFile}", $sSource, 1 )
  284. );
  285. }
  286. /**
  287. * Write data to files
  288. * @name _storeData
  289. * @type method
  290. * @access protected
  291. * @param string filename
  292. * @param string content
  293. * @return bool success
  294. * @syntax Object->_storeData( string file, string content );
  295. */
  296. protected function _storeData( $sFile, $sContent )
  297. {
  298. return $this->call( "/System/File/write", $sFile, $sContent );
  299. }
  300. /**
  301. * get the name of the compiled resource
  302. * @name _getCompileName
  303. * @type method
  304. * @access protected
  305. * @param string template
  306. * @param string reference [optional]
  307. * @return string compiled name
  308. * @syntax Object->_getCompileName( string template [, string reference ] );
  309. */
  310. protected function _getCompileName( $sTemplate, $sReference="" )
  311. {
  312. $sBase = basename( $sTemplate );
  313. return md5( "{$sTemplate}/{$sReference}-" . substr( $sBase, 0, strPos( $sBase, "." ) ) ) . ( !empty( $sReference ) ? "-{$sReference}" : "" ) . ".gen.php";
  314. }
  315. /**
  316. * get the latest update timestamp from all dependencies
  317. * @name _getDependencyUpdateTime
  318. * @type method
  319. * @access protected
  320. * @param string filename
  321. * @return number timestamp
  322. * @syntax Object->_getDependencyUpdateTime( string cachefile );
  323. */
  324. protected function _getDependencyUpdateTime( $sCacheFile )
  325. {
  326. if ( !file_exists( "{$this->_compilepath}/dep/" . md5( $sCacheFile ) ) )
  327. return time();
  328. $aDependency = unserialize( file_get_contents( "{$this->_compilepath}/dep/" . md5( $sCacheFile ) ) );
  329. $nLatest = 0;
  330. foreach( $aDependency as $sFileName )
  331. $nLatest = max( $nLatest, filemtime( $sFileName ) );
  332. return $nLatest;
  333. }
  334. /**
  335. * get the timestamp of the compilation
  336. * @name _getCompileUpdateTime
  337. * @type method
  338. * @access protected
  339. * @param string filename
  340. * @return number timestamp
  341. * @syntax Object->_getCompileUpdateTime( string cachefile );
  342. */
  343. protected function _getCompileUpdateTime( $sCacheFile )
  344. {
  345. if ( !file_exists( "{$this->_compilepath}/{$sCacheFile}" ) )
  346. return false;
  347. $this->_compiletime = filemtime( "{$this->_compilepath}/{$sCacheFile}" );
  348. return $this->_compiletime;
  349. }
  350. }
  351. ?>