PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/eztemplate/classes/eztemplatecompiler.php

http://github.com/ezsystems/ezpublish
PHP | 3062 lines | 2846 code | 52 blank | 164 comment | 57 complexity | c047e75a2bfe14fff4494bd5c7e93446 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * File containing the eZTemplateCompiler class.
  4. *
  5. * @copyright Copyright (C) eZ Systems AS. All rights reserved.
  6. * @license For full copyright and license information view LICENSE file distributed with this source code.
  7. * @version //autogentag//
  8. * @package lib
  9. */
  10. /*!
  11. \class eZTemplateCompiler eztemplatecompiler.php
  12. \brief Creates compiled PHP code from templates to speed up template usage.
  13. Various optimizations that can be done are:
  14. Data:
  15. - Is constant, generate static data
  16. - Is variable, generate direct variable extraction
  17. - Has operators
  18. - Has attributes
  19. Attributes:
  20. - Is constant, generate static data
  21. Operators:
  22. - Supports input
  23. - Supports output
  24. - Supports parameters
  25. - Generates static data (true, false)
  26. - Custom PHP code
  27. - Modifies template variables, if possible name which ones. Allows
  28. for caching of variables in the script.
  29. Functions:
  30. - Supports parameters
  31. - Supports children (set? no, section? yes)
  32. - Generates static data (ldelim,rdelim)
  33. - Children usage, no result(set-block) | copy(let,default) | dynamic(conditional, repeated etc.)
  34. - Children tree, requires original tree | allows custom processing
  35. - Custom PHP code
  36. - Deflate/transform tree, create new non-nested tree (let, default)
  37. - Modifies template variables, if possible name which ones. Allows
  38. for caching of variables in the script.
  39. */
  40. class eZTemplateCompiler
  41. {
  42. const CODE_DATE = 1074699607;
  43. /*!
  44. \static
  45. Returns the prefix for file names
  46. */
  47. static function TemplatePrefix()
  48. {
  49. $templatePrefix = '';
  50. $ini = eZINI::instance();
  51. if ( $ini->variable( 'TemplateSettings', 'TemplateCompression' ) == 'enabled' )
  52. {
  53. $templatePrefix = 'compress.zlib://';
  54. }
  55. return $templatePrefix;
  56. }
  57. /*!
  58. \static
  59. Sets/unsets various compiler settings. To set a setting add a key in the \a $settingsMap
  60. with the wanted value, to unset it use \c null as the value.
  61. The following values can be set.
  62. - compile - boolean, whether to compile templates or not
  63. - comments - boolean, whether to include comments in templates
  64. - accumulators - boolean, whether to include debug accumulators in templates
  65. - timingpoints - boolean, whether to include debug timingpoints in templates
  66. - fallbackresource - boolean, whether to include the fallback resource code
  67. - nodeplacement - boolean, whether to include information on placement of all nodes
  68. - execution - boolean, whether to execute the compiled templates or not
  69. - generate - boolean, whether to always generate the compiled files, or only when template is changed
  70. - compilation-directory - string, where to place compiled files, the path will be relative from the
  71. eZ Publish directory and not the var/cache directory.
  72. */
  73. static function setSettings( $settingsMap )
  74. {
  75. $existingMap = array();
  76. if ( isset( $GLOBALS['eZTemplateCompilerSettings'] ) )
  77. {
  78. $existingMap = $GLOBALS['eZTemplateCompilerSettings'];
  79. }
  80. $GLOBALS['eZTemplateCompilerSettings'] = array_merge( $existingMap, $settingsMap );
  81. }
  82. /*!
  83. \static
  84. \return true if template compiling is enabled.
  85. \note To change this setting edit settings/site.ini and locate the group TemplateSettings and the entry TemplateCompile.
  86. */
  87. static function isCompilationEnabled()
  88. {
  89. if ( isset( $GLOBALS['eZSiteBasics'] ) )
  90. {
  91. $siteBasics = $GLOBALS['eZSiteBasics'];
  92. if ( isset( $siteBasics['no-cache-adviced'] ) and
  93. $siteBasics['no-cache-adviced'] )
  94. {
  95. return false;
  96. }
  97. }
  98. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['compile'] ) and
  99. $GLOBALS['eZTemplateCompilerSettings']['compile'] !== null )
  100. {
  101. return $GLOBALS['eZTemplateCompilerSettings']['compile'];
  102. }
  103. $ini = eZINI::instance();
  104. $compilationEnabled = $ini->variable( 'TemplateSettings', 'TemplateCompile' ) == 'enabled';
  105. return $compilationEnabled;
  106. }
  107. /*!
  108. \static
  109. \return true if template compilation should include comments.
  110. */
  111. static function isCommentsEnabled()
  112. {
  113. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['comments'] ) and
  114. $GLOBALS['eZTemplateCompilerSettings']['comments'] !== null )
  115. {
  116. return $GLOBALS['eZTemplateCompilerSettings']['comments'];
  117. }
  118. $ini = eZINI::instance();
  119. $commentsEnabled = $ini->variable( 'TemplateSettings', 'CompileComments' ) == 'enabled';
  120. return $commentsEnabled;
  121. }
  122. /*!
  123. \static
  124. \return true if template compilation should run in development mode.
  125. When in development mode the system will perform additional checks, e.g. for
  126. modification time of compiled file vs original source file.
  127. This mode is quite useful for development since it requires less
  128. clear-cache calls but has additional file checks and should be turned off
  129. for live sites.
  130. */
  131. static function isDevelopmentModeEnabled()
  132. {
  133. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['development_mode'] ) and
  134. $GLOBALS['eZTemplateCompilerSettings']['development_mode'] !== null )
  135. {
  136. return $GLOBALS['eZTemplateCompilerSettings']['development_mode'];
  137. }
  138. $ini = eZINI::instance();
  139. $developmentModeEnabled = $ini->variable( 'TemplateSettings', 'DevelopmentMode' ) == 'enabled';
  140. return $developmentModeEnabled;
  141. }
  142. /*!
  143. \static
  144. \return true if template compilation should include debug accumulators.
  145. */
  146. static function isAccumulatorsEnabled()
  147. {
  148. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['accumulators'] ) and
  149. $GLOBALS['eZTemplateCompilerSettings']['accumulators'] !== null )
  150. {
  151. return $GLOBALS['eZTemplateCompilerSettings']['accumulators'];
  152. }
  153. $ini = eZINI::instance();
  154. $enabled = $ini->variable( 'TemplateSettings', 'CompileAccumulators' ) == 'enabled';
  155. return $enabled;
  156. }
  157. /*!
  158. \static
  159. \return true if template compilation should include debug timing points.
  160. */
  161. static function isTimingPointsEnabled()
  162. {
  163. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['timingpoints'] ) and
  164. $GLOBALS['eZTemplateCompilerSettings']['timingpoints'] !== null )
  165. {
  166. return $GLOBALS['eZTemplateCompilerSettings']['timingpoints'];
  167. }
  168. $ini = eZINI::instance();
  169. $enabled = $ini->variable( 'TemplateSettings', 'CompileTimingPoints' ) == 'enabled';
  170. return $enabled;
  171. }
  172. /*!
  173. \static
  174. \return true if resource fallback code should be included.
  175. */
  176. static function isFallbackResourceCodeEnabled()
  177. {
  178. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['fallbackresource'] ) and
  179. $GLOBALS['eZTemplateCompilerSettings']['fallbackresource'] !== null )
  180. {
  181. return $GLOBALS['eZTemplateCompilerSettings']['fallbackresource'];
  182. }
  183. $ini = eZINI::instance();
  184. $enabled = $ini->variable( 'TemplateSettings', 'CompileResourceFallback' ) == 'enabled';
  185. return $enabled;
  186. }
  187. /*!
  188. \static
  189. \return true if template compilation should include comments.
  190. */
  191. static function isNodePlacementEnabled()
  192. {
  193. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['nodeplacement'] ) and
  194. $GLOBALS['eZTemplateCompilerSettings']['nodeplacement'] !== null )
  195. {
  196. return $GLOBALS['eZTemplateCompilerSettings']['nodeplacement'];
  197. }
  198. $ini = eZINI::instance();
  199. $nodePlacementEnabled = $ini->variable( 'TemplateSettings', 'CompileNodePlacements' ) == 'enabled';
  200. return $nodePlacementEnabled;
  201. }
  202. /*!
  203. \static
  204. \return true if the compiled template execution is enabled.
  205. */
  206. static function isExecutionEnabled()
  207. {
  208. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['execution'] ) and
  209. $GLOBALS['eZTemplateCompilerSettings']['execution'] !== null )
  210. {
  211. return $GLOBALS['eZTemplateCompilerSettings']['execution'];
  212. }
  213. $ini = eZINI::instance();
  214. $execution = $ini->variable( 'TemplateSettings', 'CompileExecution' ) == 'enabled';
  215. return $execution;
  216. }
  217. /*!
  218. \static
  219. \return true if template compilation should always be run even if a sufficient compilation already exists.
  220. */
  221. static function alwaysGenerate()
  222. {
  223. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['generate'] ) and
  224. $GLOBALS['eZTemplateCompilerSettings']['generate'] !== null )
  225. {
  226. return $GLOBALS['eZTemplateCompilerSettings']['generate'];
  227. }
  228. $ini = eZINI::instance();
  229. $alwaysGenerate = $ini->variable( 'TemplateSettings', 'CompileAlwaysGenerate' ) == 'enabled';
  230. return $alwaysGenerate;
  231. }
  232. /*!
  233. \static
  234. \return true if template node tree named \a $treeName should be included the compiled template.
  235. */
  236. static function isTreeEnabled( $treeName )
  237. {
  238. $ini = eZINI::instance();
  239. $treeList = $ini->variable( 'TemplateSettings', 'CompileIncludeNodeTree' );
  240. return in_array( $treeName, $treeList );
  241. }
  242. /*!
  243. \static
  244. \return the directory for compiled templates.
  245. */
  246. static function compilationDirectory()
  247. {
  248. if ( isset( $GLOBALS['eZTemplateCompilerSettings']['compilation-directory'] ) and
  249. $GLOBALS['eZTemplateCompilerSettings']['compilation-directory'] !== null )
  250. {
  251. return $GLOBALS['eZTemplateCompilerSettings']['compilation-directory'];
  252. }
  253. $compilationDirectory =& $GLOBALS['eZTemplateCompilerDirectory'];
  254. if ( !isset( $compilationDirectory ) )
  255. {
  256. $ini = eZINI::instance();
  257. $shareTemplates = $ini->hasVariable( 'TemplateSettings', 'ShareCompiledTemplates' ) ?
  258. $ini->variable( 'TemplateSettings', 'ShareCompiledTemplates' ) == 'enabled' :
  259. false;
  260. if ( $shareTemplates &&
  261. $ini->hasVariable( 'TemplateSettings', 'SharedCompiledTemplatesDir' ) &&
  262. trim( $ini->variable( 'TemplateSettings', 'SharedCompiledTemplatesDir' ) ) != '' )
  263. {
  264. $compilationDirectory = eZDir::path( array( $ini->variable( 'TemplateSettings', 'SharedCompiledTemplatesDir' ) ) );
  265. }
  266. else
  267. {
  268. $compilationDirectory = eZDir::path( array( eZSys::cacheDirectory(), 'template/compiled' ) );
  269. }
  270. }
  271. return $compilationDirectory;
  272. }
  273. /*!
  274. Creates the name for the compiled template and returns it.
  275. The name conists of original filename with the md5 of the key and charset appended.
  276. */
  277. static function compilationFilename( $key, $resourceData )
  278. {
  279. $internalCharset = eZTextCodec::internalCharset();
  280. $templateFilepath = $resourceData['template-filename'];
  281. $extraName = '';
  282. if ( preg_match( "#^.+/(.*)\.tpl$#", $templateFilepath, $matches ) )
  283. $extraName = $matches[1] . '-';
  284. else if ( preg_match( "#^(.*)\.tpl$#", $templateFilepath, $matches ) )
  285. $extraName = $matches[1] . '-';
  286. $accessText = false;
  287. if ( isset( $GLOBALS['eZCurrentAccess']['name'] ) )
  288. $accessText = '-' . $GLOBALS['eZCurrentAccess']['name'];
  289. $locale = eZLocale::instance();
  290. $language = $locale->localeFullCode();
  291. $http = eZHTTPTool::instance();
  292. $useFullUrlText = $http->UseFullUrl ? 'full' : 'relative';
  293. $pageLayoutVariable = "";
  294. if ( isset( $GLOBALS['eZCustomPageLayout'] ) )
  295. $pageLayoutVariable = $GLOBALS['eZCustomPageLayout'];
  296. $ini = eZINI::instance();
  297. $shareTemplates = $ini->hasVariable( 'TemplateSettings', 'ShareCompiledTemplates' ) ?
  298. $ini->variable( 'TemplateSettings', 'ShareCompiledTemplates' ) == 'enabled' :
  299. false;
  300. if ( $shareTemplates )
  301. $cacheFileKey = $key . '-' . $language;
  302. else
  303. $cacheFileKey = $key . '-' . $internalCharset . '-' . $language . '-' . $useFullUrlText . $accessText . "-" . $pageLayoutVariable . '-' . eZSys::indexFile();
  304. $cacheFileName = $extraName . md5( $cacheFileKey ) . '.php';
  305. return $cacheFileName;
  306. }
  307. /*!
  308. \static
  309. \return true if the compiled template with the key \a $key exists.
  310. A compiled template is found usable when it exists and has a timestamp
  311. higher or equal to \a $timestamp.
  312. */
  313. static function hasCompiledTemplate( $key, $timestamp, &$resourceData )
  314. {
  315. if ( !eZTemplateCompiler::isCompilationEnabled() )
  316. return false;
  317. if ( eZTemplateCompiler::alwaysGenerate() )
  318. return false;
  319. $cacheFileName = eZTemplateCompiler::compilationFilename( $key, $resourceData );
  320. $php = new eZPHPCreator( eZTemplateCompiler::compilationDirectory(), $cacheFileName, eZTemplateCompiler::TemplatePrefix() );
  321. $canRestore = $php->canRestore( $timestamp );
  322. $uri = false;
  323. if ( $canRestore )
  324. eZDebugSetting::writeDebug( 'eztemplate-compile', "Cache hit for uri '$uri' with key '$key'", 'eZTemplateCompiler::hasCompiledTemplate' );
  325. else
  326. eZDebugSetting::writeDebug( 'eztemplate-compile', "Cache miss for uri '$uri' with key '$key'", 'eZTemplateCompiler::hasCompiledTemplate' );
  327. return $canRestore;
  328. }
  329. /**
  330. * Tries to execute the compiled template. Returns true if successful,
  331. * returns false if the compilation is disabled or the compiled template
  332. * does not exist.
  333. *
  334. * @param eZTemplate $tpl
  335. * @param array $textElements
  336. * @param string $key
  337. * @param array $resourceData
  338. * @param $rootNamespace
  339. * @param $currentNamespace
  340. *
  341. * @throws eZTemplateFailedExecutingCompiledTemplate if the compiled
  342. * template could not be executed
  343. * @return bool
  344. */
  345. static function executeCompilation( $tpl, &$textElements, $key, &$resourceData,
  346. $rootNamespace, $currentNamespace )
  347. {
  348. if ( !eZTemplateCompiler::isCompilationEnabled() )
  349. return false;
  350. if ( !eZTemplateCompiler::isExecutionEnabled() )
  351. return false;
  352. $cacheFileName = eZTemplateCompiler::compilationFilename( $key, $resourceData );
  353. $resourceData['use-comments'] = eZTemplateCompiler::isCommentsEnabled();
  354. $directory = eZTemplateCompiler::compilationDirectory();
  355. $phpScript = eZDir::path( array( $directory, $cacheFileName ) );
  356. if ( file_exists( $phpScript ) )
  357. {
  358. $text = false;
  359. $helperStatus = eZTemplateCompiler::executeCompilationHelper( $phpScript, $text,
  360. $tpl, $key, $resourceData,
  361. $rootNamespace, $currentNamespace );
  362. if ( $helperStatus )
  363. {
  364. $textElements[] = $text;
  365. return true;
  366. }
  367. else
  368. {
  369. eZDebug::writeError(
  370. "Failed executing compiled template '$phpScript',"
  371. . " if this file is valid, then the error is probably"
  372. . " related to APC usage, try restarting the webserver",
  373. __METHOD__
  374. );
  375. throw new eZTemplateFailedExecutingCompiledTemplate(
  376. "Failed executing a compiled template, see error.log for details"
  377. );
  378. }
  379. }
  380. else
  381. eZDebug::writeError( "Unknown compiled template '$phpScript'", __METHOD__ );
  382. return false;
  383. }
  384. /*!
  385. Helper function for executeCompilation. Will execute the script \a $phpScript and
  386. set the result text in \a $text.
  387. The parameters \a $tpl, \a $resourceData, \a $rootNamespace and \a $currentNamespace
  388. are passed to the executed template compilation script.
  389. \return true if a text result was created.
  390. */
  391. static function executeCompilationHelper( $phpScript, &$text,
  392. $tpl, $key, &$resourceData,
  393. $rootNamespace, $currentNamespace )
  394. {
  395. $vars =& $tpl->Variables;
  396. /* We use $setArray to detect if execution failed, and not $text,
  397. * because an empty template does not return any $text and this is not
  398. * an error. */
  399. $setArray = null;
  400. $namespaceStack = array();
  401. $tpl->createLocalVariablesList();
  402. include( eZTemplateCompiler::TemplatePrefix() . $phpScript );
  403. $tpl->unsetLocalVariables();
  404. $tpl->destroyLocalVariablesList();
  405. if ( $setArray !== null )
  406. {
  407. return true;
  408. }
  409. return false;
  410. }
  411. /*!
  412. \static
  413. Generates the cache which will be used for handling optimized processing using the key \a $key.
  414. \note Each call to this will set the PHP time limit to 30
  415. \return false if the cache does not exist.
  416. */
  417. static function compileTemplate( $tpl, $key, &$resourceData )
  418. {
  419. if ( !eZTemplateCompiler::isCompilationEnabled() )
  420. return false;
  421. $resourceData['use-comments'] = eZTemplateCompiler::isCommentsEnabled();
  422. $cacheFileName = eZTemplateCompiler::compilationFilename( $key, $resourceData );
  423. $resourceData['uniqid'] = md5( $resourceData['template-filename']. uniqid( "ezp". getmypid(), true ) );
  424. // Time limit #1:
  425. // We reset the time limit to 30 seconds to ensure that templates
  426. // have enough time to compile
  427. // However if time limit is unlimited (0) we leave it be
  428. // Time limit will also be reset after subtemplates are compiled
  429. $maxExecutionTime = ini_get( 'max_execution_time' );
  430. if ( $maxExecutionTime != 0 && $maxExecutionTime < 30 )
  431. {
  432. @set_time_limit( 30 );
  433. }
  434. $rootNode =& $resourceData['root-node'];
  435. if ( !$rootNode )
  436. return false;
  437. $GLOBALS['eZTemplateCompilerResourceCache'][$resourceData['template-filename']] =& $resourceData;
  438. $useComments = eZTemplateCompiler::isCommentsEnabled();
  439. if ( !$resourceData['test-compile'] )
  440. {
  441. eZTemplateCompiler::createCommonCompileTemplate();
  442. }
  443. /* Check if we need to disable the generation of spacing for the compiled templates */
  444. $ini = eZINI::instance();
  445. $spacing = 'disabled';
  446. if ( $ini->variable( 'TemplateSettings', 'UseFormatting' ) == 'enabled' )
  447. {
  448. $spacing = 'enabled';
  449. }
  450. $php = new eZPHPCreator( eZTemplateCompiler::compilationDirectory(), $cacheFileName,
  451. eZTemplateCompiler::TemplatePrefix(), array( 'spacing' => $spacing ) );
  452. $php->addComment( 'URI: ' . $resourceData['uri'] );
  453. $php->addComment( 'Filename: ' . $resourceData['template-filename'] );
  454. $php->addComment( 'Timestamp: ' . $resourceData['time-stamp'] . ' (' . date( 'D M j G:i:s T Y', $resourceData['time-stamp'] ) . ')' );
  455. $php->addCodePiece("\$oldSetArray_{$resourceData['uniqid']} = isset( \$setArray ) ? \$setArray : array();\n".
  456. "\$setArray = array();\n");
  457. // Code to decrement include level of the templates
  458. $php->addCodePiece( "\$tpl->Level++;\n" );
  459. $php->addCodePiece( "if ( \$tpl->Level > $tpl->MaxLevel )\n".
  460. "{\n".
  461. "\$text = \$tpl->MaxLevelWarning;".
  462. "\$tpl->Level--;\n".
  463. "return;\n".
  464. "}\n" );
  465. if ( $resourceData['locales'] && count( $resourceData['locales'] ) )
  466. {
  467. $php->addComment( 'Locales: ' . join( ', ', $resourceData['locales'] ) );
  468. $php->addCodePiece(
  469. '$locales = array( "'. join( '", "', $resourceData['locales'] ) . "\" );\n".
  470. '$oldLocale_'. $resourceData['uniqid']. ' = setlocale( LC_CTYPE, null );'. "\n".
  471. '$currentLocale_'. $resourceData['uniqid']. ' = setlocale( LC_CTYPE, $locales );'. "\n"
  472. );
  473. }
  474. // $php->addCodePiece( "print( \"" . $resourceData['template-filename'] . " ($cacheFileName)<br/>\n\" );" );
  475. if ( $useComments )
  476. {
  477. $templateFilename = $resourceData['template-filename'];
  478. if ( file_exists( $templateFilename ) )
  479. {
  480. $fd = fopen( $templateFilename, 'rb' );
  481. if ( $fd )
  482. {
  483. $templateText = fread( $fd, filesize( $templateFilename ) );
  484. $php->addComment( "Original code:\n" . $templateText );
  485. fclose( $fd );
  486. }
  487. }
  488. }
  489. $php->addVariable( 'eZTemplateCompilerCodeDate', eZTemplateCompiler::CODE_DATE );
  490. $php->addCodePiece( "if ( !defined( 'EZ_TEMPLATE_COMPILER_COMMON_CODE' ) )\n" );
  491. $php->addInclude( eZTemplateCompiler::compilationDirectory() . '/common.php', eZPHPCreator::INCLUDE_ONCE_STATEMENT, array( 'spacing' => 4 ) );
  492. $php->addSpace();
  493. if ( eZTemplateCompiler::isAccumulatorsEnabled() )
  494. {
  495. $php->addCodePiece( "eZDebug::accumulatorStart( 'template_compiled_execution', 'template_total', 'Template compiled execution', true );\n" );
  496. }
  497. if ( eZTemplateCompiler::isTimingPointsEnabled() )
  498. {
  499. $php->addCodePiece( "eZDebug::addTimingPoint( 'Script start $cacheFileName' );\n" );
  500. }
  501. // $php->addCodePiece( "if ( !isset( \$vars ) )\n \$vars =& \$tpl->Variables;\n" );
  502. // $php->addSpace();
  503. $parameters = array();
  504. $textName = eZTemplateCompiler::currentTextName( $parameters );
  505. // $php->addCodePiece( "if ( !isset( \$$textName ) )\n \$$textName = '';\n" );
  506. // $php->addSpace();
  507. $transformedTree = array();
  508. eZTemplateCompiler::processNodeTransformation( $useComments, $php, $tpl, $rootNode, $resourceData, $transformedTree );
  509. if ( $ini->variable( 'TemplateSettings', 'TemplateOptimization' ) == 'enabled' )
  510. {
  511. /* Retrieve class information for the attribute lookup table */
  512. if ( isset( $resourceData['handler']->Keys ) and isset( $resourceData['handler']->Keys['class'] ) ) {
  513. $resourceData['class-info'] = eZTemplateOptimizer::fetchClassDeclaration( $resourceData['handler']->Keys['class'] );
  514. }
  515. /* Run the optimizations */
  516. eZTemplateOptimizer::optimize( $useComments, $php, $tpl, $transformedTree, $resourceData );
  517. }
  518. $staticTree = array();
  519. eZTemplateCompiler::processStaticOptimizations( $useComments, $php, $tpl, $transformedTree, $resourceData, $staticTree );
  520. $combinedTree = array();
  521. eZTemplateCompiler::processNodeCombining( $useComments, $php, $tpl, $staticTree, $resourceData, $combinedTree );
  522. $finalTree = $combinedTree;
  523. if ( !eZTemplateCompiler::isNodePlacementEnabled() )
  524. eZTemplateCompiler::processRemoveNodePlacement( $finalTree );
  525. eZTemplateCompiler::generatePHPCode( $useComments, $php, $tpl, $finalTree, $resourceData );
  526. if ( eZTemplateCompiler::isTreeEnabled( 'final' ) )
  527. $php->addVariable( 'finalTree', $finalTree, eZPHPCreator::VARIABLE_ASSIGNMENT, array( 'full-tree' => true ) );
  528. if ( eZTemplateCompiler::isTreeEnabled( 'combined' ) )
  529. $php->addVariable( 'combinedTree', $combinedTree, eZPHPCreator::VARIABLE_ASSIGNMENT, array( 'full-tree' => true ) );
  530. if ( eZTemplateCompiler::isTreeEnabled( 'static' ) )
  531. $php->addVariable( 'staticTree', $staticTree, eZPHPCreator::VARIABLE_ASSIGNMENT, array( 'full-tree' => true ) );
  532. if ( eZTemplateCompiler::isTreeEnabled( 'transformed' ) )
  533. $php->addVariable( 'transformedTree', $transformedTree, eZPHPCreator::VARIABLE_ASSIGNMENT, array( 'full-tree' => true ) );
  534. if ( eZTemplateCompiler::isTreeEnabled( 'original' ) )
  535. $php->addVariable( 'originalTree', $rootNode, eZPHPCreator::VARIABLE_ASSIGNMENT, array( 'full-tree' => true ) );
  536. if ( eZTemplateCompiler::isTimingPointsEnabled() )
  537. $php->addCodePiece( "eZDebug::addTimingPoint( 'Script end $cacheFileName' );\n" );
  538. if ( eZTemplateCompiler::isAccumulatorsEnabled() )
  539. $php->addCodePiece( "eZDebug::accumulatorStop( 'template_compiled_execution', true );\n" );
  540. if ( $resourceData['locales'] && count( $resourceData['locales'] ) )
  541. {
  542. $php->addCodePiece(
  543. 'setlocale( LC_CTYPE, $oldLocale_'. $resourceData['uniqid']. ' );'. "\n"
  544. );
  545. }
  546. $php->addCodePiece('$setArray = $oldSetArray_'. $resourceData['uniqid']. ";\n");
  547. // Code to decrement include level of the templates
  548. $php->addCodePiece("\$tpl->Level--;\n" );
  549. /*
  550. // dump names of all defined PHP variables
  551. $php->addCodePiece( "echo \"defined vars in $resourceData[uri]:<br/><pre>\\n\";\n" );
  552. $php->addCodePiece( 'foreach ( array_keys( get_defined_vars() ) as $var_name ) echo "- $var_name\n";' );
  553. // dump tpl vars
  554. $php->addCodePiece( 'echo "\n-----------------------------------------------------------\nvars: ";' );
  555. $php->addCodePiece( 'var_dump( $vars );' );
  556. $php->addCodePiece( 'echo "</pre><hr/>\n";' );
  557. */
  558. if ( !$resourceData['test-compile'] )
  559. {
  560. $php->store( true );
  561. }
  562. return true;
  563. }
  564. /*!
  565. Iterates over the template node tree and tries to combine multiple static siblings
  566. into one element. The original tree is specified in \a $node and the new
  567. combined tree will be present in \a $newNode.
  568. \sa processNodeCombiningChildren
  569. */
  570. static function processNodeCombining( $useComments, $php, $tpl, &$node, &$resourceData, &$newNode )
  571. {
  572. $nodeType = $node[0];
  573. if ( $nodeType == eZTemplate::NODE_ROOT )
  574. {
  575. $children = $node[1];
  576. $newNode[0] = $nodeType;
  577. $newNode[1] = false;
  578. if ( $children )
  579. {
  580. eZTemplateCompiler::processNodeCombiningChildren( $useComments, $php, $tpl, $children, $resourceData, $newNode );
  581. }
  582. }
  583. else
  584. $tpl->error( 'processNodeCombining', "Unknown root type $nodeType, should be " . eZTemplate::NODE_ROOT );
  585. }
  586. /*!
  587. Does node combining on the children \a $nodeChildren.
  588. \sa processNodeCombining
  589. */
  590. static function processNodeCombiningChildren( $useComments, $php, $tpl, &$nodeChildren, &$resourceData, &$parentNode )
  591. {
  592. $newNodeChildren = array();
  593. $lastNode = false;
  594. foreach ( $nodeChildren as $node )
  595. {
  596. $newNode = false;
  597. if ( !isset( $node[0] ) )
  598. continue;
  599. $nodeType = $node[0];
  600. if ( $nodeType == eZTemplate::NODE_ROOT )
  601. {
  602. $children = $node[1];
  603. $newNode = array( $nodeType,
  604. false );
  605. if ( $children )
  606. {
  607. eZTemplateCompiler::processNodeCombiningChildren( $useComments, $php, $tpl, $children, $resourceData, $newNode );
  608. }
  609. }
  610. else if ( $nodeType == eZTemplate::NODE_TEXT )
  611. {
  612. $text = $node[2];
  613. $placement = $node[3];
  614. $newNode = array( $nodeType,
  615. false,
  616. $text,
  617. $placement );
  618. eZTemplateCompiler::combineStaticNodes( $tpl, $resourceData, $lastNode, $newNode );
  619. }
  620. else if ( $nodeType == eZTemplate::NODE_VARIABLE )
  621. {
  622. $variableCustom = $node[1];
  623. $variableData = $node[2];
  624. $variablePlacement = $node[3];
  625. $variableParameters = false;
  626. $dataInspection = eZTemplateCompiler::inspectVariableData( $tpl,
  627. $variableData, $variablePlacement,
  628. $resourceData );
  629. $newNode = $node;
  630. $newNode[1] = $variableCustom;
  631. unset( $dataInspection );
  632. eZTemplateCompiler::combineStaticNodes( $tpl, $resourceData, $lastNode, $newNode );
  633. }
  634. else if ( $nodeType == eZTemplate::NODE_FUNCTION )
  635. {
  636. $functionChildren = $node[1];
  637. $functionName = $node[2];
  638. $functionParameters = $node[3];
  639. $functionPlacement = $node[4];
  640. $newNode = array( $nodeType,
  641. false,
  642. $functionName,
  643. $functionParameters,
  644. $functionPlacement );
  645. if ( isset( $node[5] ) )
  646. $newNode[5] = $node[5];
  647. if ( is_array( $functionChildren ) )
  648. {
  649. eZTemplateCompiler::processNodeCombiningChildren( $useComments, $php, $tpl,
  650. $functionChildren, $resourceData, $newNode );
  651. }
  652. }
  653. else
  654. $newNode = $node;
  655. if ( $lastNode != false )
  656. {
  657. $newNodeChildren[] = $lastNode;
  658. $lastNode = false;
  659. }
  660. if ( $newNode != false )
  661. $lastNode = $newNode;
  662. }
  663. if ( $lastNode != false )
  664. {
  665. $newNodeChildren[] = $lastNode;
  666. $lastNode = false;
  667. }
  668. $parentNode[1] = $newNodeChildren;
  669. }
  670. /*!
  671. Tries to combine the node \a $lastNode and the node \a $newNode
  672. into one new text node. If possible the new node is created in \a $newNode
  673. and \a $lastNode will be set to \c false.
  674. Combining nodes only works for text nodes and variable nodes without
  675. variable lookup, attributes and operators.
  676. */
  677. static function combineStaticNodes( $tpl, &$resourceData, &$lastNode, &$newNode )
  678. {
  679. if ( $lastNode == false or
  680. $newNode == false )
  681. return false;
  682. $lastNodeType = $lastNode[0];
  683. $newNodeType = $newNode[0];
  684. if ( !in_array( $lastNodeType, array( eZTemplate::NODE_TEXT,
  685. eZTemplate::NODE_VARIABLE ) ) or
  686. !in_array( $newNodeType, array( eZTemplate::NODE_TEXT,
  687. eZTemplate::NODE_VARIABLE ) ) )
  688. return false;
  689. if ( $lastNodeType == eZTemplate::NODE_VARIABLE )
  690. {
  691. if ( is_array( $lastNode[1] ) )
  692. return false;
  693. $lastDataInspection = eZTemplateCompiler::inspectVariableData( $tpl,
  694. $lastNode[2], $lastNode[3],
  695. $resourceData );
  696. if ( !$lastDataInspection['is-constant'] or
  697. $lastDataInspection['is-variable'] or
  698. $lastDataInspection['has-attributes'] or
  699. $lastDataInspection['has-operators'] )
  700. return false;
  701. if ( isset( $lastNode[4] ) and
  702. isset( $lastNode[4]['text-result'] ) and
  703. !$lastNode[4]['text-result'] )
  704. return false;
  705. }
  706. if ( $newNodeType == eZTemplate::NODE_VARIABLE )
  707. {
  708. if ( is_array( $newNode[1] ) )
  709. return false;
  710. $newDataInspection = eZTemplateCompiler::inspectVariableData( $tpl,
  711. $newNode[2], $newNode[3],
  712. $resourceData );
  713. if ( !$newDataInspection['is-constant'] or
  714. $newDataInspection['is-variable'] or
  715. $newDataInspection['has-attributes'] or
  716. $newDataInspection['has-operators'] )
  717. return false;
  718. if ( isset( $newNode[4] ) and
  719. isset( $newNode[4]['text-result'] ) and
  720. !$newNode[4]['text-result'] )
  721. return false;
  722. if ( isset( $newNode[1] ) and
  723. $newNode[1] !== false )
  724. return false;
  725. }
  726. $textElements = array();
  727. $lastNodeData = eZTemplateCompiler::staticNodeData( $lastNode );
  728. $newNodeData = eZTemplateCompiler::staticNodeData( $newNode );
  729. $tpl->appendElementText( $textElements, $lastNodeData, false, false );
  730. $tpl->appendElementText( $textElements, $newNodeData, false, false );
  731. $newData = implode( '', $textElements );
  732. $newPlacement = $lastNode[3];
  733. if ( !is_array( $newPlacement ) )
  734. {
  735. $newPlacement = $newNode[3];
  736. }
  737. else
  738. {
  739. $newPlacement[1][0] = $newNode[3][1][0]; // Line end
  740. $newPlacement[1][1] = $newNode[3][1][1]; // Column end
  741. $newPlacement[1][2] = $newNode[3][1][2]; // Position end
  742. }
  743. $lastNode = false;
  744. $newNode = array( eZTemplate::NODE_TEXT,
  745. false,
  746. $newData,
  747. $newPlacement );
  748. }
  749. /*!
  750. \return the static data for the node \a $node or \c false if
  751. no data could be fetched.
  752. Will only return data from text nodes and variables nodes
  753. without variable lookup, attribute lookup or operators.
  754. */
  755. static function staticNodeData( $node )
  756. {
  757. $nodeType = $node[0];
  758. if ( $nodeType == eZTemplate::NODE_TEXT )
  759. {
  760. return $node[2];
  761. }
  762. else if ( $nodeType == eZTemplate::NODE_VARIABLE )
  763. {
  764. $data = $node[2];
  765. if ( is_array( $data ) and
  766. count( $data ) > 0 )
  767. {
  768. $dataType = $data[0][0];
  769. if ( $dataType == eZTemplate::TYPE_STRING or
  770. $dataType == eZTemplate::TYPE_NUMERIC or
  771. $dataType == eZTemplate::TYPE_IDENTIFIER or
  772. $dataType == eZTemplate::TYPE_ARRAY or
  773. $dataType == eZTemplate::TYPE_BOOLEAN )
  774. {
  775. return $data[0][1];
  776. }
  777. }
  778. }
  779. return null;
  780. }
  781. /*!
  782. Iterates over the items in the tree \a $node and tries to extract static data
  783. from operators which supports it.
  784. */
  785. static function processStaticOptimizations( $useComments, $php, $tpl, &$node, &$resourceData, &$newNode )
  786. {
  787. $nodeType = $node[0];
  788. if ( $nodeType == eZTemplate::NODE_ROOT )
  789. {
  790. $children = $node[1];
  791. $newNode[0] = $nodeType;
  792. $newNode[1] = false;
  793. if ( $children )
  794. {
  795. $newNode[1] = array();
  796. foreach ( $children as $child )
  797. {
  798. $newChild = array();
  799. eZTemplateCompiler::processStaticOptimizations( $useComments, $php, $tpl, $child, $resourceData, $newChild );
  800. $newNode[1][] = $newChild;
  801. }
  802. }
  803. }
  804. else if ( $nodeType == eZTemplate::NODE_TEXT )
  805. {
  806. $text = $node[2];
  807. $placement = $node[3];
  808. $newNode[0] = $nodeType;
  809. $newNode[1] = false;
  810. $newNode[2] = $text;
  811. $newNode[3] = $placement;
  812. }
  813. else if ( $nodeType == eZTemplate::NODE_VARIABLE )
  814. {
  815. $variableCustom = $node[1];
  816. $variableData = $node[2];
  817. $variablePlacement = $node[3];
  818. $dataInspection = eZTemplateCompiler::inspectVariableData( $tpl,
  819. $variableData, $variablePlacement,
  820. $resourceData );
  821. if ( isset( $dataInspection['new-data'] ) )
  822. {
  823. $variableData = $dataInspection['new-data'];
  824. }
  825. $newNode = $node;
  826. $newNode[1] = $variableCustom;
  827. $newNode[2] = $variableData;
  828. unset( $dataInspection );
  829. }
  830. else if ( $nodeType == eZTemplate::NODE_FUNCTION )
  831. {
  832. $functionChildren = $node[1];
  833. $functionName = $node[2];
  834. $functionParameters = $node[3];
  835. $functionPlacement = $node[4];
  836. $newFunctionChildren = array();
  837. if ( is_array( $functionChildren ) )
  838. {
  839. foreach ( $functionChildren as $functionChild )
  840. {
  841. $newChild = array();
  842. eZTemplateCompiler::processStaticOptimizations( $useComments, $php, $tpl,
  843. $functionChild, $resourceData, $newChild );
  844. $newFunctionChildren[] = $newChild;
  845. }
  846. $functionChildren = $newFunctionChildren;
  847. }
  848. $newFunctionParameters = array();
  849. if ( $functionParameters )
  850. {
  851. foreach ( $functionParameters as $functionParameterName => $functionParameterData )
  852. {
  853. $dataInspection = eZTemplateCompiler::inspectVariableData( $tpl,
  854. $functionParameterData, false,
  855. $resourceData );
  856. if ( isset( $dataInspection['new-data'] ) )
  857. {
  858. $functionParameterData = $dataInspection['new-data'];
  859. }
  860. $newFunctionParameters[$functionParameterName] = $functionParameterData;
  861. }
  862. $functionParameters = $newFunctionParameters;
  863. }
  864. $newNode[0] = $nodeType;
  865. $newNode[1] = $functionChildren;
  866. $newNode[2] = $functionName;
  867. $newNode[3] = $functionParameters;
  868. $newNode[4] = $functionPlacement;
  869. if ( isset( $node[5] ) )
  870. $newNode[5] = $node[5];
  871. }
  872. else
  873. $newNode = $node;
  874. }
  875. /*!
  876. Iterates over the template node tree \a $node and returns a new transformed
  877. tree in \a $newNode.
  878. \sa processNodeTransformationRoot, processNodeTransformationChild
  879. */
  880. static function processNodeTransformation( $useComments, $php, $tpl, &$node, &$resourceData, &$newNode )
  881. {
  882. $newNode = eZTemplateCompiler::processNodeTransformationRoot( $useComments, $php, $tpl, $node, $resourceData );
  883. }
  884. /*!
  885. Iterates over the nodes \a $nodes and does transformation on them.
  886. \sa processNodeTransformationChildren
  887. \note This method can be called from operator and functions as long as they have the \a $privateData parameter.
  888. */
  889. static function processNodeTransformationNodes( $tpl, &$node, &$nodes, &$privateData )
  890. {
  891. $useComments = $privateData['use-comments'];
  892. $php =& $privateData['php-creator'];
  893. $resourceData =& $privateData['resource-data'];
  894. return eZTemplateCompiler::processNodeTransformationChildren( $useComments, $php, $tpl, $node, $nodes, $resourceData );
  895. }
  896. /*!
  897. Iterates over the children \a $children and does transformation on them.
  898. \sa processNodeTransformation, processNodeTransformationChild
  899. */
  900. static function processNodeTransformationChildren( $useComments, $php, $tpl, &$node, &$children, &$resourceData )
  901. {
  902. if ( $children )
  903. {
  904. $newChildren = array();
  905. foreach ( $children as $childNode )
  906. {
  907. $newChildNode = eZTemplateCompiler::processNodeTransformationChild( $useComments, $php, $tpl, $childNode, $resourceData );
  908. if ( !$newChildNode )
  909. $newChildren[] = $childNode;
  910. else
  911. $newChildren = array_merge( $newChildren, $newChildNode );
  912. }
  913. if ( count( $newChildren ) > 0 )
  914. return $newChildren;
  915. }
  916. return $children;
  917. }
  918. /*!
  919. Iterates over the children of the root node \a $node and does transformation on them.
  920. \sa processNodeTransformation, processNodeTransformationChild
  921. */
  922. static function processNodeTransformationRoot( $useComments, $php, $tpl, &$node, &$resourceData )
  923. {
  924. $nodeType = $node[0];
  925. if ( $nodeType == eZTemplate::NODE_ROOT )
  926. {
  927. $children = $node[1];
  928. $newNode = array( $nodeType,
  929. false );
  930. if ( $children )
  931. {
  932. $newChildren = array();
  933. foreach ( $children as $childNode )
  934. {
  935. $newChildNode = eZTemplateCompiler::processNodeTransformationChild( $useComments, $php, $tpl, $childNode, $resourceData );
  936. if ( !$newChildNode )
  937. $newChildren[] = $childNode;
  938. else
  939. $newChildren = array_merge( $newChildren, $newChildNode );
  940. }
  941. if ( count( $newChildren ) > 0 )
  942. $newNode[1] = $newChildren;
  943. }
  944. return $newNode;
  945. }
  946. else
  947. $tpl->error( 'processNodeTransformation', "Unknown root type $nodeType, should be " . eZTemplate::NODE_ROOT );
  948. return false;
  949. }
  950. /*!
  951. Iterates over the children of the function node \a $node and transforms the tree.
  952. If the node is not a function it will return \c false.
  953. \sa processNodeTransformationRoot, processNodeTransformationChild
  954. */
  955. static function processNodeTransformationChild( $useComments, $php, $tpl, &$node, &$resourceData )
  956. {
  957. $nodeType = $node[0];
  958. if ( $nodeType == eZTemplate::NODE_FUNCTION )
  959. {
  960. $nodeCopy = $node;
  961. $functionChildren = $node[1];
  962. $functionName = $node[2];
  963. $functionParameters = $node[3];
  964. $functionPlacement = $node[4];
  965. if ( !isset( $tpl->Functions[$functionName] ) )
  966. return false;
  967. if ( is_array( $tpl->Functions[$functionName] ) )
  968. {
  969. $tpl->loadAndRegisterFunctions( $tpl->Functions[$functionName] );
  970. }
  971. $functionObject =& $tpl->Functions[$functionName];
  972. if ( is_object( $functionObject ) )
  973. {
  974. $hasTransformationSupport = false;
  975. $transformChildren = true;
  976. $transformParameters = false;
  977. if ( method_exists( $functionObject, 'functionTemplateHints' ) )
  978. {
  979. $hints = $functionObject->functionTemplateHints();
  980. if ( isset( $hints[$functionName] ) and
  981. isset( $hints[$functionName]['tree-transformation'] ) and
  982. $hints[$functionName]['tree-transformation'] )
  983. $hasTransformationSupport = true;
  984. if ( isset( $hints[$functionName] ) and
  985. isset( $hints[$functionName]['transform-children'] ) )
  986. $transformChildren = $hints[$functionName]['transform-children'];
  987. if ( isset( $hints[$functionName] ) and
  988. isset( $hints[$functionName]['transform-parameters'] ) )
  989. $transformParameters = $hints[$functionName]['transform-parameters'];
  990. }
  991. if ( $hasTransformationSupport and
  992. method_exists( $functionObject, 'templateNodeTransformation' ) )
  993. {
  994. if ( $transformChildren and
  995. $functionChildren )
  996. {
  997. $newChildren = array();
  998. foreach ( $functionChildren as $childNode )
  999. {
  1000. $newChildNode = eZTemplateCompiler::processNodeTransformationChild( $useComments, $php, $tpl, $childNode, $resourceData );
  1001. if ( !$newChildNode )
  1002. $newChildren[] = $childNode;
  1003. else if ( !is_array( $newChildNode ) )
  1004. $newChildren[] = $newChildNode;
  1005. else
  1006. $newChildren = array_merge( $newChildren, $newChildNode );
  1007. }
  1008. if ( count( $newChildren ) > 0 )
  1009. $node[1] = $newChildren;
  1010. }
  1011. if ( $transformParameters and
  1012. $functionParameters )
  1013. {
  1014. $newParameters = array();
  1015. foreach ( $functionParameters as $parameterName => $parameterElementList )
  1016. {
  1017. $elementTree = $parameterElementList;
  1018. $elementList = $elementTree;
  1019. $newParamNode = eZTemplateCompiler::processElementTransformationChild( $useComments, $php, $tpl, $node,
  1020. $elementTree, $elementList, $resourceData );
  1021. if ( !$newParamNode || !is_array( $newParamNode ) )
  1022. $newParameters[$parameterName] = $parameterElementList;
  1023. else
  1024. $newParameters[$parameterName] = $newParamNode;
  1025. }
  1026. if ( count( $newParameters ) > 0 )
  1027. {
  1028. $node[3] = $newParameters;
  1029. $functionParameters = $newParameters;
  1030. }
  1031. }
  1032. $privateData = array( 'use-comments' => $useComments,
  1033. 'php-creator' => $php,
  1034. 'resource-data' => &$resourceData );
  1035. $newNodes = $functionObject->templateNodeTransformation( $functionName, $node,
  1036. $tpl, $functionParameters, $privateData );
  1037. unset( $privateData );
  1038. if ( !$newNodes )
  1039. {
  1040. $node = $nodeCopy;
  1041. $node[1] = $functionChildren;
  1042. return false;
  1043. return $node;
  1044. }
  1045. return $newNodes;
  1046. }
  1047. }
  1048. else if ( $resourceData['test-compile'] )
  1049. {
  1050. $tpl->warning( '', "Function '$functionName' is not registered.", $functionPlacement );
  1051. }
  1052. return false;
  1053. }
  1054. else if ( $nodeType == eZTemplate::NODE_VARIABLE )
  1055. {
  1056. $elementTree = $node[2];
  1057. $elementList = $elementTree;
  1058. $newParameterElements = eZTemplateCompiler::processElementTransformationChild( $useComments, $php, $tpl, $node,
  1059. $elementTree, $elementList, $resourceData );
  1060. if ( $newParameterElements )
  1061. {
  1062. $newNode = $node;
  1063. $newNode[2] = $newParameterElements;
  1064. $newNodes = array( $newNode );
  1065. return $newNodes;
  1066. }
  1067. }
  1068. else if ( $nodeType == eZTemplate::NODE_ROOT )
  1069. {
  1070. return eZTemplateCompiler::processNodeTransformationRoot( $useComments, $php, $tpl, $node, $resourceData );
  1071. }
  1072. else
  1073. return false;
  1074. }
  1075. /*!
  1076. Iterates over the element list \a $elements and transforms them.
  1077. \sa processElementTransformationChild
  1078. */
  1079. static function processElementTransformationList( $tpl, &$node, $elements, &$privateData )
  1080. {
  1081. $useComments = $privateData['use-comments'];
  1082. $php =& $privateData['php-creator'];
  1083. $resourceData =& $privateData['resource-data'];
  1084. $elementTree = $elements;
  1085. $newElements = eZTemplateCompiler::processElementTransformationChild( $useComments, $php, $tpl, $node,
  1086. $elementTree, $elements, $resourceData );
  1087. if ( $newElements )
  1088. return $newElements;
  1089. return $elements;
  1090. }
  1091. /*!
  1092. Iterates over the children of the function node \a $node and transforms the tree.
  1093. If the node is not a function it will return \c false.
  1094. \sa processNodeTransformationRoot, processNodeTransformationCh…

Large files files are truncated, but you can click here to view the full file