PageRenderTime 65ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/eztemplate/classes/eztemplate.php

http://github.com/ezsystems/ezpublish
PHP | 2720 lines | 1862 code | 229 blank | 629 comment | 266 complexity | 066270e1b01b7237178b8dde811833f7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * File containing the eZTemplate 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. /*! \defgroup eZTemplate Template system */
  11. /*!
  12. \class eZTemplate eztemplate.php
  13. \ingroup eZTemplate
  14. \brief The main manager for templates
  15. The template systems allows for separation of code and
  16. layout by moving the layout part into template files. These
  17. template files are parsed and processed with template variables set
  18. by the PHP code.
  19. The template system in itself is does not do much, it parses template files
  20. according to a rule set sets up a tree hierarchy and process the data
  21. using functions and operators. The standard template system comes with only
  22. a few functions and no operators, it is meant for these functions and operators
  23. to be specified by the users of the template system. But for simplicity a few
  24. help classes is available which can be easily enabled.
  25. The classes are:
  26. - eZTemplateDelimitFunction - Inserts the left and right delimiter which are normally parsed.
  27. - eZTemplateSectionFunction - Allows for conditional blocks and loops.
  28. - eZTemplateIncludeFunction - Includes external templates
  29. - eZTemplateSequenceFunction - Creates sequences arrays
  30. - eZTemplateSwitchFunction - Conditional output of template
  31. - eZTemplatePHPOperator - Allows for easy redirection of operator names to PHP functions.
  32. - eZTemplateLocaleOperator - Allows for locale conversions.
  33. - eZTemplateArrayOperator - Creates arrays
  34. - eZTemplateAttributeOperator - Displays contents of template variables, useful for debugging
  35. - eZTemplateImageOperator - Converts text to image
  36. - eZTemplateLogicOperator - Various logical operators for boolean handling
  37. - eZTemplateUnitOperator - Unit conversion and display
  38. To enable these functions and operator use registerFunction and registerOperator.
  39. In keeping with the spirit of being simple the template system does not know how
  40. to get the template files itself. Instead it relies on resource handlers, these
  41. handlers fetches the template files using different kind of transport mechanism.
  42. For simplicity a default resource class is available, eZTemplateFileResource fetches
  43. templates from the filesystem.
  44. The parser process consists of three passes, each pass adds a new level of complexity.
  45. The first pass strips text from template blocks which starts with a left delimiter and
  46. ends with a right delimiter (default is { and } ), and places them in an array.
  47. The second pass iterates the text and block elements and removes newlines from
  48. text before function blocks and text after function blocks.
  49. The third pass builds the tree according the function rules.
  50. Processing is done by iterating over the root of the tree, if a text block is found
  51. the text is appended to the result text. If a variable or contant is it's data is extracted
  52. and any operators found are run on it before fetching the result and appending it to
  53. the result text. If a function is found the function is called with the parameters
  54. and it's up to the function handle children if any.
  55. Constants and template variables will usually be called variables since there's little
  56. difference. A template variable expression will start with a $ and consists of a
  57. namespace (optional) a name and attribues(optional). The variable expression
  58. \verbatim $root:var.attr1 \endverbatim exists in the "root" namespace, has the name "var" and uses the
  59. attribute "attr1". Some functions will create variables on demand, to avoid name conflicts
  60. namespaces were introduced, each function will place the new variables in a namespace
  61. specified in the template file. Attribues are used for fetching parts of the variable,
  62. for instance an element in an array or data in an object. Since the syntax is the
  63. same for arrays and objects the PHP code can use simple arrays when speed is required,
  64. the template code will not care.
  65. A different syntax is also available when you want to access an attribute using a variable.
  66. For instance \verbatim $root:var[$attr_var] \endverbatim, if the variable $attr_var contains "attr1" it would
  67. access the same attribute as in the first example.
  68. The syntax for operators is a | and a name, optionally parameters can be specified with
  69. ( and ) delimited with ,. Valid operators are \verbatim |upcase, |l10n(date) \endverbatim.
  70. Functions look a lot like HTML/XML tags. The function consists of a name and parameters
  71. which are assigned using the param=value syntax. Some parameters may be required while
  72. others may be optionally, the exact behaviour is specified by each function.
  73. Valid functions are \verbatim "section name=abc loop=4" \endverbatim
  74. Example of usage:
  75. \code
  76. // Init template
  77. $tpl = eZTemplate::instance();
  78. $tpl->registerOperators( new eZTemplatePHPOperator( array( "upcase" => "strtoupper",
  79. "reverse" => "strrev" ) ) );
  80. $tpl->registerOperators( new eZTemplateLocaleOperator() );
  81. $tpl->registerFunction( "section", new eZTemplateSectionFunction( "section" ) );
  82. $tpl->registerFunctions( new eZTemplateDelimitFunction() );
  83. $tpl->setVariable( "my_var", "{this value set by variable}", "test" );
  84. $tpl->setVariable( "my_arr", array( "1st", "2nd", "third", "fjerde" ) );
  85. $tpl->setVariable( "multidim", array( array( "a", "b" ),
  86. array( "c", "d" ),
  87. array( "e", "f" ),
  88. array( "g", "h" ) ) );
  89. class mytest
  90. {
  91. function mytest( $n, $s )
  92. {
  93. $this->n = $n;
  94. $this->s = $s;
  95. }
  96. function hasAttribute( $attr )
  97. {
  98. return ( $attr == "name" || $attr == "size" );
  99. }
  100. function attribute( $attr )
  101. {
  102. switch ( $attr )
  103. {
  104. case "name";
  105. return $this->n;
  106. case "size";
  107. return $this->s;
  108. default:
  109. $retAttr = null;
  110. return $retAttr;
  111. }
  112. }
  113. }
  114. $tpl->setVariable( "multidim_obj", array( new mytest( "jan", 200 ),
  115. new mytest( "feb", 200 ),
  116. new mytest( "john", 200 ),
  117. new mytest( "doe", 50 ) ) );
  118. $tpl->setVariable( "curdate", time() );
  119. $tpl->display( "lib/eztemplate/example/test.tpl" );
  120. // test.tpl
  121. {section name=outer loop=4}
  122. 123
  123. {delimit}::{/delimit}
  124. {/section}
  125. {literal test=1} This is some {blah arg1="" arg2="abc" /} {/literal}
  126. <title>This is a test</title>
  127. <table border="1">
  128. <tr><th>{$test:my_var}
  129. {"some text!!!"|upcase|reverse}</th></tr>
  130. {section name=abc loop=$my_arr}
  131. <tr><td>{$abc:item}</td></tr>
  132. {/section}
  133. </table>
  134. <table border="1">
  135. {section name=outer loop=$multidim}
  136. <tr>
  137. {section name=inner loop=$outer:item}
  138. <td>{$inner:item}</td>
  139. {/section}
  140. </tr>
  141. {/section}
  142. </table>
  143. <table border="1">
  144. {section name=outer loop=$multidim_obj}
  145. <tr>
  146. <td>{$outer:item.name}</td>
  147. <td>{$outer:item.size}</td>
  148. </tr>
  149. {/section}
  150. </table>
  151. {section name=outer loop=$nonexistingvar}
  152. <b><i>Dette skal ikke vises</b></i>
  153. {section-else}
  154. <b><i>This is shown when the {ldelim}$loop{rdelim} variable is non-existant</b></i>
  155. {/section}
  156. Denne koster {1.4|l10n(currency)}<br>
  157. {-123456789|l10n(number)}<br>
  158. {$curdate|l10n(date)}<br>
  159. {$curdate|l10n(shortdate)}<br>
  160. {$curdate|l10n(time)}<br>
  161. {$curdate|l10n(shorttime)}<br>
  162. {include file="test2.tpl"/}
  163. \endcode
  164. */
  165. class eZTemplate
  166. {
  167. const RESOURCE_FETCH = 1;
  168. const RESOURCE_QUERY = 2;
  169. const ELEMENT_TEXT = 1;
  170. const ELEMENT_SINGLE_TAG = 2;
  171. const ELEMENT_NORMAL_TAG = 3;
  172. const ELEMENT_END_TAG = 4;
  173. const ELEMENT_VARIABLE = 5;
  174. const ELEMENT_COMMENT = 6;
  175. const NODE_ROOT = 1;
  176. const NODE_TEXT = 2;
  177. const NODE_VARIABLE = 3;
  178. const NODE_FUNCTION = 4;
  179. const NODE_OPERATOR = 5;
  180. const NODE_INTERNAL = 100;
  181. const NODE_INTERNAL_CODE_PIECE = 101;
  182. const NODE_INTERNAL_VARIABLE_SET = 105;
  183. const NODE_INTERNAL_VARIABLE_UNSET = 102;
  184. const NODE_INTERNAL_NAMESPACE_CHANGE = 103;
  185. const NODE_INTERNAL_NAMESPACE_RESTORE = 104;
  186. const NODE_INTERNAL_WARNING = 120;
  187. const NODE_INTERNAL_ERROR = 121;
  188. const NODE_INTERNAL_RESOURCE_ACQUISITION = 140;
  189. const NODE_OPTIMIZED_RESOURCE_ACQUISITION = 141;
  190. const NODE_INTERNAL_OUTPUT_ASSIGN = 150;
  191. const NODE_INTERNAL_OUTPUT_READ = 151;
  192. const NODE_INTERNAL_OUTPUT_INCREASE = 152;
  193. const NODE_INTERNAL_OUTPUT_DECREASE = 153;
  194. const NODE_INTERNAL_OUTPUT_SPACING_INCREASE = 160;
  195. const NODE_INTERNAL_SPACING_DECREASE = 161;
  196. const NODE_OPTIMIZED_INIT = 201;
  197. const NODE_USER_CUSTOM = 1000;
  198. const TYPE_VOID = 0;
  199. const TYPE_STRING = 1;
  200. const TYPE_NUMERIC = 2;
  201. const TYPE_IDENTIFIER = 3;
  202. const TYPE_VARIABLE = 4;
  203. const TYPE_ATTRIBUTE = 5;
  204. const TYPE_OPERATOR = 6;
  205. const TYPE_BOOLEAN = 7;
  206. const TYPE_ARRAY = 8;
  207. const TYPE_DYNAMIC_ARRAY = 9;
  208. const TYPE_INTERNAL = 100;
  209. const TYPE_INTERNAL_CODE_PIECE = 101;
  210. const TYPE_PHP_VARIABLE = 102;
  211. const TYPE_OPTIMIZED_NODE = 201;
  212. const TYPE_OPTIMIZED_ARRAY_LOOKUP = 202;
  213. const TYPE_OPTIMIZED_CONTENT_CALL = 203;
  214. const TYPE_OPTIMIZED_ATTRIBUTE_LOOKUP = 204;
  215. const TYPE_INTERNAL_STOP = 999;
  216. const TYPE_STRING_BIT = 1;
  217. const TYPE_NUMERIC_BIT = 2;
  218. const TYPE_IDENTIFIER_BIT = 4;
  219. const TYPE_VARIABLE_BIT = 8;
  220. const TYPE_ATTRIBUTE_BIT = 16;
  221. const TYPE_OPERATOR_BIT = 32;
  222. const TYPE_NONE = 0;
  223. const TYPE_ALL = 63;
  224. const TYPE_BASIC = 47;
  225. const TYPE_MODIFIER_MASK = 48;
  226. const NAMESPACE_SCOPE_GLOBAL = 1;
  227. const NAMESPACE_SCOPE_LOCAL = 2;
  228. const NAMESPACE_SCOPE_RELATIVE = 3;
  229. const DEBUG_INTERNALS = false;
  230. const FILE_ERRORS = 1;
  231. /**
  232. * Intializes the template with left and right delimiters being { and }, and a file resource.
  233. * The literal tag "literal" is also registered.
  234. */
  235. public function __construct()
  236. {
  237. $this->Tree = array( eZTemplate::NODE_ROOT, false );
  238. $this->LDelim = "{";
  239. $this->RDelim = "}";
  240. $this->IncludeText = array();
  241. $this->IncludeOutput = array();
  242. $this->registerLiteral( "literal" );
  243. $res = new eZTemplateFileResource();
  244. $this->DefaultResource = $res;
  245. $this->registerResource( $res );
  246. $this->Resources = array();
  247. $this->Text = null;
  248. $this->IsCachingAllowed = true;
  249. $this->resetErrorLog();
  250. $this->AutoloadPathList = array( 'lib/eztemplate/classes/' );
  251. $this->Variables = array();
  252. $this->LocalVariablesNamesStack = array();
  253. $this->CurrentLocalVariablesNames = null;
  254. $this->Functions = array();
  255. $this->FunctionAttributes = array();
  256. $this->TestCompile = false;
  257. $ini = eZINI::instance( 'template.ini' );
  258. if ( $ini->hasVariable( 'ControlSettings', 'MaxLevel' ) )
  259. $this->MaxLevel = $ini->variable( 'ControlSettings', 'MaxLevel' );
  260. $this->MaxLevelWarning = ezpI18n::tr( 'lib/template',
  261. 'The maximum nesting level of %max has been reached. The execution is stopped to avoid infinite recursion.',
  262. '',
  263. array( '%max' => $this->MaxLevel ) );
  264. eZDebug::createAccumulatorGroup( 'template_total', 'Template Total' );
  265. $this->TemplatesUsageStatistics = array();
  266. // Array of templates which are used in a single fetch()
  267. $this->TemplateFetchList = array();
  268. $this->ForeachCounter = 0;
  269. $this->ForCounter = 0;
  270. $this->WhileCounter = 0;
  271. $this->DoCounter = 0;
  272. $this->ElseifCounter = 0;
  273. }
  274. /*!
  275. Returns the left delimiter being used.
  276. */
  277. function leftDelimiter()
  278. {
  279. return $this->LDelim;
  280. }
  281. /*!
  282. Returns the right delimiter being used.
  283. */
  284. function rightDelimiter()
  285. {
  286. return $this->RDelim;
  287. }
  288. /*!
  289. Sets the left delimiter.
  290. */
  291. function setLeftDelimiter( $delim )
  292. {
  293. $this->LDelim = $delim;
  294. }
  295. /*!
  296. Sets the right delimiter.
  297. */
  298. function setRightDelimiter( $delim )
  299. {
  300. $this->RDelim = $delim;
  301. }
  302. /*!
  303. Fetches the result of the template file and displays it.
  304. If $template is supplied it will load this template file first.
  305. */
  306. function display( $template = false, $extraParameters = false )
  307. {
  308. $output = $this->fetch( $template, $extraParameters );
  309. if ( $this->ShowDetails )
  310. {
  311. echo '<h1>Result:</h1>' . "\n";
  312. echo '<hr/>' . "\n";
  313. }
  314. echo "$output";
  315. if ( $this->ShowDetails )
  316. {
  317. echo '<hr/>' . "\n";
  318. }
  319. if ( $this->ShowDetails )
  320. {
  321. echo "<h1>Template data:</h1>";
  322. echo "<p class=\"filename\">" . $template . "</p>";
  323. echo "<pre class=\"example\">" . htmlspecialchars( $this->Text ) . "</pre>";
  324. reset( $this->IncludeText );
  325. while ( ( $key = key( $this->IncludeText ) ) !== null )
  326. {
  327. $item = $this->IncludeText[$key];
  328. echo "<p class=\"filename\">" . $key . "</p>";
  329. echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>";
  330. next( $this->IncludeText );
  331. }
  332. echo "<h1>Result text:</h1>";
  333. echo "<p class=\"filename\">" . $template . "</p>";
  334. echo "<pre class=\"example\">" . htmlspecialchars( $output ) . "</pre>";
  335. reset( $this->IncludeOutput );
  336. while ( ( $key = key( $this->IncludeOutput ) ) !== null )
  337. {
  338. $item = $this->IncludeOutput[$key];
  339. echo "<p class=\"filename\">" . $key . "</p>";
  340. echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>";
  341. next( $this->IncludeOutput );
  342. }
  343. }
  344. }
  345. /*!
  346. * Initialize list of local variables for the current template.
  347. * The list contains only names of variables.
  348. */
  349. function createLocalVariablesList()
  350. {
  351. $this->LocalVariablesNamesStack[] = array();
  352. $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1];
  353. }
  354. /*!
  355. * Check if the given local variable exists.
  356. */
  357. function hasLocalVariable( $varName, $rootNamespace )
  358. {
  359. return ( array_key_exists( $rootNamespace, $this->CurrentLocalVariablesNames ) &&
  360. array_key_exists( $varName, $this->CurrentLocalVariablesNames[$rootNamespace] ) );
  361. }
  362. /*!
  363. * Create a local variable.
  364. */
  365. function setLocalVariable( $varName, $varValue, $rootNamespace )
  366. {
  367. $this->CurrentLocalVariablesNames[$rootNamespace][$varName] = 1;
  368. $this->setVariable( $varName, $varValue, $rootNamespace );
  369. }
  370. /*!
  371. * Destroy a local variable.
  372. */
  373. function unsetLocalVariable( $varName, $rootNamespace )
  374. {
  375. if ( !$this->hasLocalVariable( $varName, $rootNamespace ) )
  376. return;
  377. $this->unsetVariable( $varName, $rootNamespace );
  378. unset( $this->CurrentLocalVariablesNames[$rootNamespace][$varName] );
  379. }
  380. /*!
  381. * Destroy all local variables defined in the current template.
  382. */
  383. function unsetLocalVariables()
  384. {
  385. foreach ( $this->CurrentLocalVariablesNames as $ns => $vars )
  386. {
  387. foreach ( $vars as $var => $val )
  388. $this->unsetLocalVariable( $var, $ns );
  389. }
  390. }
  391. /*!
  392. * Destroy list of local variables defined in the current (innermost) template.
  393. */
  394. function destroyLocalVariablesList()
  395. {
  396. array_pop( $this->LocalVariablesNamesStack );
  397. if ( $this->LocalVariablesNamesStack )
  398. $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1];
  399. else
  400. unset( $this->CurrentLocalVariablesNames );
  401. }
  402. /*!
  403. Tries to fetch the result of the template file and returns it.
  404. If $template is supplied it will load this template file first.
  405. */
  406. function fetch( $template = false, $extraParameters = false, $returnResourceData = false )
  407. {
  408. $this->resetErrorLog();
  409. // Reset fetch list when a new fetch is started
  410. $this->TemplateFetchList = array();
  411. eZDebug::accumulatorStart( 'template_total' );
  412. eZDebug::accumulatorStart( 'template_load', 'template_total', 'Template load' );
  413. $root = null;
  414. if ( is_string( $template ) )
  415. {
  416. $resourceData = $this->loadURIRoot( $template, true, $extraParameters );
  417. if ( $resourceData and
  418. $resourceData['root-node'] !== null )
  419. $root =& $resourceData['root-node'];
  420. }
  421. eZDebug::accumulatorStop( 'template_load' );
  422. if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
  423. {
  424. $savedLocale = setlocale( LC_CTYPE, null );
  425. setlocale( LC_CTYPE, $resourceData['locales'] );
  426. }
  427. $text = "";
  428. if ( $root !== null or
  429. $resourceData['compiled-template'] )
  430. {
  431. if ( $this->ShowDetails )
  432. eZDebug::addTimingPoint( "Process" );
  433. eZDebug::accumulatorStart( 'template_processing', 'template_total', 'Template processing' );
  434. $templateCompilationUsed = false;
  435. if ( $resourceData['compiled-template'] )
  436. {
  437. $textElements = array();
  438. if ( $this->executeCompiledTemplate( $resourceData, $textElements, "", "", $extraParameters ) )
  439. {
  440. $text = implode( '', $textElements );
  441. $templateCompilationUsed = true;
  442. }
  443. }
  444. if ( !$templateCompilationUsed )
  445. {
  446. if ( eZTemplate::isDebugEnabled() )
  447. {
  448. $fname = $resourceData['template-filename'];
  449. eZDebug::writeDebug( "FETCH START URI: $template, $fname" );
  450. }
  451. $this->process( $root, $text, "", "" );
  452. if ( eZTemplate::isDebugEnabled() )
  453. eZDebug::writeDebug( "FETCH END URI: $template, $fname" );
  454. }
  455. eZDebug::accumulatorStop( 'template_processing' );
  456. if ( $this->ShowDetails )
  457. eZDebug::addTimingPoint( "Process done" );
  458. }
  459. eZDebug::accumulatorStop( 'template_total' );
  460. if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
  461. {
  462. setlocale( LC_CTYPE, $savedLocale );
  463. }
  464. if ( $returnResourceData )
  465. {
  466. $resourceData['result_text'] = $text;
  467. return $resourceData;
  468. }
  469. return $text;
  470. }
  471. function process( $root, &$text, $rootNamespace, $currentNamespace )
  472. {
  473. $this->createLocalVariablesList();
  474. $textElements = array();
  475. $this->processNode( $root, $textElements, $rootNamespace, $currentNamespace );
  476. if ( is_array( $textElements ) )
  477. $text = implode( '', $textElements );
  478. else
  479. $text = $textElements;
  480. $this->unsetLocalVariables();
  481. $this->destroyLocalVariablesList();
  482. }
  483. function processNode( $node, &$textElements, $rootNamespace, $currentNamespace )
  484. {
  485. $rslt = null;
  486. $nodeType = $node[0];
  487. if ( $nodeType == eZTemplate::NODE_ROOT )
  488. {
  489. $children = $node[1];
  490. if ( $children )
  491. {
  492. foreach ( $children as $child )
  493. {
  494. $this->processNode( $child, $textElements, $rootNamespace, $currentNamespace );
  495. if ( !is_array( $textElements ) )
  496. eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::root' );
  497. }
  498. }
  499. }
  500. else if ( $nodeType == eZTemplate::NODE_TEXT )
  501. {
  502. $textElements[] = $node[2];
  503. }
  504. else if ( $nodeType == eZTemplate::NODE_VARIABLE )
  505. {
  506. $variableData = $node[2];
  507. $variablePlacement = $node[3];
  508. $this->processVariable( $textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace );
  509. if ( !is_array( $textElements ) )
  510. eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::variable' );
  511. }
  512. else if ( $nodeType == eZTemplate::NODE_FUNCTION )
  513. {
  514. $functionChildren = $node[1];
  515. $functionName = $node[2];
  516. $functionParameters = $node[3];
  517. $functionPlacement = $node[4];
  518. $rslt = $this->processFunction( $functionName, $textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace );
  519. if ( !is_array( $textElements ) )
  520. eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . "::function( '$functionName' )" );
  521. }
  522. return $rslt;
  523. }
  524. function processVariable( &$textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace )
  525. {
  526. $value = $this->elementValue( $variableData, $rootNamespace, $currentNamespace, $variablePlacement );
  527. $this->appendElementText( $textElements, $value, $rootNamespace, $currentNamespace );
  528. }
  529. function processFunction( $functionName, &$textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace )
  530. {
  531. // Note: This code piece is replicated in the eZTemplateCompiler,
  532. // if this code is changed the replicated code must be updated as well.
  533. $func = $this->Functions[$functionName];
  534. if ( is_array( $func ) )
  535. {
  536. $this->loadAndRegisterFunctions( $this->Functions[$functionName] );
  537. $func = $this->Functions[$functionName];
  538. }
  539. if ( isset( $func ) and
  540. is_object( $func ) )
  541. {
  542. if ( eZTemplate::isMethodDebugEnabled() )
  543. eZDebug::writeDebug( "START FUNCTION: $functionName" );
  544. $value = $func->process( $this, $textElements, $functionName, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace );
  545. if ( eZTemplate::isMethodDebugEnabled() )
  546. eZDebug::writeDebug( "END FUNCTION: $functionName" );
  547. return $value;
  548. }
  549. else
  550. {
  551. $this->warning( "", "Function \"$functionName\" is not registered" );
  552. return null;
  553. }
  554. }
  555. function fetchFunctionObject( $functionName )
  556. {
  557. $func = $this->Functions[$functionName];
  558. if ( is_array( $func ) )
  559. {
  560. $this->loadAndRegisterFunctions( $this->Functions[$functionName] );
  561. $func = $this->Functions[$functionName];
  562. }
  563. return $func;
  564. }
  565. /*!
  566. Loads the template using the URI $uri and parses it.
  567. \return The root node of the tree if \a $returnResourceData is false,
  568. if \c true the entire resource data structure.
  569. */
  570. function load( $uri, $extraParameters = false, $returnResourceData = false )
  571. {
  572. $resourceData = $this->loadURIRoot( $uri, true, $extraParameters );
  573. if ( !$resourceData or
  574. $resourceData['root-node'] === null )
  575. {
  576. $retValue = null;
  577. return $retValue;
  578. }
  579. else
  580. return $resourceData['root-node'];
  581. }
  582. function parse( $sourceText, &$rootElement, $rootNamespace, &$resourceData )
  583. {
  584. $parser = eZTemplateMultiPassParser::instance();
  585. $parser->parse( $this, $sourceText, $rootElement, $rootNamespace, $resourceData );
  586. }
  587. function loadURIData( $resourceObject, $uri, $resourceName, $template, &$extraParameters, $displayErrors = true )
  588. {
  589. $resourceData = $this->resourceData( $resourceObject, $uri, $resourceName, $template );
  590. $resourceData['text'] = null;
  591. $resourceData['root-node'] = null;
  592. $resourceData['compiled-template'] = false;
  593. $resourceData['time-stamp'] = null;
  594. $resourceData['key-data'] = null;
  595. $resourceData['locales'] = null;
  596. if ( !$resourceObject->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters ) )
  597. {
  598. $resourceData = null;
  599. if ( $displayErrors )
  600. $this->warning( "", "No template could be loaded for \"$template\" using resource \"$resourceName\"" );
  601. }
  602. return $resourceData;
  603. }
  604. /*!
  605. \static
  606. Creates a resource data structure of the parameters and returns it.
  607. This structure is passed to various parts of the template system.
  608. \note If you only have the URI you should call resourceFor() first to
  609. figure out the resource handler.
  610. */
  611. function resourceData( $resourceObject, $uri, $resourceName, $templateName )
  612. {
  613. $resourceData = array();
  614. $resourceData['uri'] = $uri;
  615. $resourceData['resource'] = $resourceName;
  616. $resourceData['template-name'] = $templateName;
  617. $resourceData['template-filename'] = $templateName;
  618. $resourceData['handler'] = $resourceObject;
  619. $resourceData['test-compile'] = $this->TestCompile;
  620. return $resourceData;
  621. }
  622. /*!
  623. Loads the template using the URI $uri and returns a structure with the text and timestamp,
  624. false otherwise.
  625. The structure keys are:
  626. - "text", the text.
  627. - "time-stamp", the timestamp.
  628. */
  629. function loadURIRoot( $uri, $displayErrors = true, &$extraParameters )
  630. {
  631. $res = "";
  632. $template = "";
  633. $resobj = $this->resourceFor( $uri, $res, $template );
  634. if ( !is_object( $resobj ) )
  635. {
  636. if ( $displayErrors )
  637. $this->warning( "", "No resource handler for \"$res\" and no default resource handler, aborting." );
  638. return null;
  639. }
  640. $canCache = true;
  641. if ( !$resobj->servesStaticData() )
  642. $canCache = false;
  643. if ( !$this->isCachingAllowed() )
  644. $canCache = false;
  645. $resourceData = $this->loadURIData( $resobj, $uri, $res, $template, $extraParameters, $displayErrors );
  646. if ( $resourceData )
  647. {
  648. eZTemplate::appendTemplateToStatisticsIfNeeded( $resourceData['template-name'], $resourceData['template-filename'] );
  649. $this->appendTemplateFetch( $resourceData['template-filename'] );
  650. if ( !$resourceData['compiled-template'] and
  651. $resourceData['root-node'] === null )
  652. {
  653. $resourceData['root-node'] = array( eZTemplate::NODE_ROOT, false );
  654. $templateText = $resourceData["text"];
  655. $keyData = $resourceData['key-data'];
  656. $this->setIncludeText( $uri, $templateText );
  657. $rootNamespace = '';
  658. $this->parse( $templateText, $resourceData['root-node'], $rootNamespace, $resourceData );
  659. if ( eZTemplate::isDebugEnabled() )
  660. {
  661. $this->appendDebugNodes( $resourceData['root-node'], $resourceData );
  662. }
  663. if ( $canCache )
  664. $resobj->setCachedTemplateTree( $keyData, $uri, $res, $template, $extraParameters, $resourceData['root-node'] );
  665. }
  666. if ( !$resourceData['compiled-template'] and
  667. $canCache and
  668. $this->canCompileTemplate( $resourceData, $extraParameters ) )
  669. {
  670. $generateStatus = $this->compileTemplate( $resourceData, $extraParameters );
  671. if ( $generateStatus )
  672. $resourceData['compiled-template'] = true;
  673. }
  674. }
  675. return $resourceData;
  676. }
  677. function processURI( $uri, $displayErrors = true, &$extraParameters,
  678. &$textElements, $rootNamespace, $currentNamespace )
  679. {
  680. $this->Level++;
  681. if ( $this->Level > $this->MaxLevel )
  682. {
  683. eZDebug::writeError( $this->MaxLevelWarning, __METHOD__ . " Level: $this->Level @ $uri" );
  684. $textElements[] = $this->MaxLevelWarning;
  685. $this->Level--;
  686. return;
  687. }
  688. $resourceData = $this->loadURIRoot( $uri, $displayErrors, $extraParameters );
  689. if ( !$resourceData or
  690. ( !$resourceData['compiled-template'] and
  691. $resourceData['root-node'] === null ) )
  692. {
  693. $this->Level--;
  694. return;
  695. }
  696. $templateCompilationUsed = false;
  697. if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
  698. {
  699. $savedLocale = setlocale( LC_CTYPE, null );
  700. setlocale( LC_CTYPE, $resourceData['locales'] );
  701. }
  702. if ( $resourceData['compiled-template'] )
  703. {
  704. if ( $this->executeCompiledTemplate( $resourceData, $textElements, $rootNamespace, $currentNamespace, $extraParameters ) )
  705. $templateCompilationUsed = true;
  706. }
  707. if ( !$templateCompilationUsed )
  708. {
  709. $text = null;
  710. if ( eZTemplate::isDebugEnabled() )
  711. {
  712. $fname = $resourceData['template-filename'];
  713. eZDebug::writeDebug( "START URI: $uri, $fname" );
  714. }
  715. $this->process( $resourceData['root-node'], $text, $rootNamespace, $currentNamespace );
  716. if ( eZTemplate::isDebugEnabled() )
  717. eZDebug::writeDebug( "END URI: $uri, $fname" );
  718. $this->setIncludeOutput( $uri, $text );
  719. $textElements[] = $text;
  720. }
  721. if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
  722. {
  723. setlocale( LC_CTYPE, $savedLocale );
  724. }
  725. $this->Level--;
  726. }
  727. function canCompileTemplate( $resourceData, &$extraParameters )
  728. {
  729. $resourceObject = $resourceData['handler'];
  730. if ( !$resourceObject )
  731. return false;
  732. $canGenerate = $resourceObject->canCompileTemplate( $this, $resourceData, $extraParameters );
  733. return $canGenerate;
  734. }
  735. /*!
  736. Validates the template file \a $file and returns \c true if the file has correct syntax.
  737. \param $returnResourceData If \c true then the returned value will be the resourcedata structure
  738. \sa compileTemplateFile(), fetch()
  739. */
  740. function validateTemplateFile( $file, $returnResourceData = false )
  741. {
  742. $this->resetErrorLog();
  743. if ( !file_exists( $file ) )
  744. return false;
  745. $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName );
  746. if ( !$resourceHandler )
  747. return false;
  748. $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName );
  749. $resourceData['key-data'] = "file:" . $file;
  750. $extraParameters = array();
  751. // Disable caching/compiling while fetchin the resource
  752. // It will be restored afterwards
  753. $isCachingAllowed = $this->IsCachingAllowed;
  754. $this->IsCachingAllowed = false;
  755. $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters );
  756. // Restore previous caching flag
  757. $this->IsCachingAllowed = $isCachingAllowed;
  758. $root =& $resourceData['root-node'];
  759. $root = array( eZTemplate::NODE_ROOT, false );
  760. $templateText = $resourceData["text"];
  761. $rootNamespace = '';
  762. $this->parse( $templateText, $root, $rootNamespace, $resourceData );
  763. if ( eZTemplate::isDebugEnabled() )
  764. {
  765. $this->appendDebugNodes( $root, $resourceData );
  766. }
  767. $result = !$this->hasErrors() and !$this->hasWarnings();
  768. if ( $returnResourceData )
  769. {
  770. $resourceData['result'] = $result;
  771. $resourceData['errors'] = $this->ErrorLog();
  772. $resourceData['warnings'] = $this->WarningLog();
  773. return $resourceData;
  774. }
  775. return $result;
  776. }
  777. /*!
  778. Compiles the template file \a $file and returns \c true if the compilation was OK.
  779. \param $returnResourceData If \c true then the returned value will be the resourcedata structure
  780. \sa validateTemplateFile(), fetch()
  781. */
  782. function compileTemplateFile( $file, $returnResourceData = false )
  783. {
  784. $this->resetErrorLog();
  785. if ( !file_exists( $file ) )
  786. return false;
  787. $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName );
  788. if ( !$resourceHandler )
  789. return false;
  790. $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName );
  791. $resourceData['key-data'] = "file:" . $file;
  792. $key = md5( $resourceData['key-data'] );
  793. $extraParameters = array();
  794. $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters );
  795. $isCompiled = false;
  796. if ( isset( $resourceData['compiled-template'] ) )
  797. $isCompiled = $resourceData['compiled-template'];
  798. if ( !$isCompiled )
  799. {
  800. $root =& $resourceData['root-node'];
  801. $root = array( eZTemplate::NODE_ROOT, false );
  802. $templateText = $resourceData["text"];
  803. $rootNamespace = '';
  804. $this->parse( $templateText, $root, $rootNamespace, $resourceData );
  805. if ( eZTemplate::isDebugEnabled() )
  806. {
  807. $this->appendDebugNodes( $root, $resourceData );
  808. }
  809. $result = eZTemplateCompiler::compileTemplate( $this, $key, $resourceData );
  810. }
  811. else
  812. {
  813. $result = true;
  814. }
  815. if ( $returnResourceData )
  816. {
  817. $resourceData['result'] = $result;
  818. return $resourceData;
  819. }
  820. return $result;
  821. }
  822. function compileTemplate( &$resourceData, &$extraParameters )
  823. {
  824. $resourceObject = $resourceData['handler'];
  825. if ( !$resourceObject )
  826. return false;
  827. $keyData = $resourceData['key-data'];
  828. $uri = $resourceData['uri'];
  829. $resourceName = $resourceData['resource'];
  830. $templatePath = $resourceData['template-name'];
  831. return $resourceObject->compileTemplate( $this, $keyData, $uri, $resourceName, $templatePath, $extraParameters, $resourceData );
  832. }
  833. function executeCompiledTemplate( &$resourceData, &$textElements, $rootNamespace, $currentNamespace, &$extraParameters )
  834. {
  835. $resourceObject = $resourceData['handler'];
  836. if ( !$resourceObject )
  837. return false;
  838. $keyData = $resourceData['key-data'];
  839. $uri = $resourceData['uri'];
  840. $resourceName = $resourceData['resource'];
  841. $templatePath = $resourceData['template-name'];
  842. $timestamp = $resourceData['time-stamp'];
  843. return $resourceObject->executeCompiledTemplate( $this, $textElements,
  844. $keyData, $uri, $resourceData, $templatePath,
  845. $extraParameters, $timestamp,
  846. $rootNamespace, $currentNamespace );
  847. }
  848. /*!
  849. Returns the resource object for URI $uri. If a resource type is specified
  850. in the URI it is extracted and set in $res. The template name is set in $template
  851. without any resource specifier. To specify a resource the name and a ":" is
  852. prepended to the URI, for instance file:my.tpl.
  853. If no resource type is found the URI the default resource handler is used.
  854. */
  855. function resourceFor( $uri, &$res, &$template )
  856. {
  857. $args = explode( ":", $uri );
  858. if ( isset( $args[1] ) )
  859. {
  860. $res = $args[0];
  861. $template = $args[1];
  862. }
  863. else
  864. $template = $uri;
  865. if ( eZTemplate::isDebugEnabled() )
  866. {
  867. eZDebug::writeNotice( "eZTemplate: Loading template \"$template\" with resource \"$res\"" );
  868. }
  869. if ( isset( $this->Resources[$res] ) and is_object( $this->Resources[$res] ) )
  870. {
  871. return $this->Resources[$res];
  872. }
  873. return $this->DefaultResource;
  874. }
  875. /*!
  876. \return The resource handler object for resource name \a $resourceName.
  877. \sa resourceFor
  878. */
  879. function resourceHandler( $resourceName )
  880. {
  881. if ( isset( $this->Resources[$resourceName] ) &&
  882. is_object( $this->Resources[$resourceName] ) )
  883. {
  884. return $this->Resources[$resourceName];
  885. }
  886. return $this->DefaultResource;
  887. }
  888. function hasChildren( &$function, $functionName )
  889. {
  890. $hasChildren = $function->hasChildren();
  891. if ( is_array( $hasChildren ) )
  892. return $hasChildren[$functionName];
  893. else
  894. return $hasChildren;
  895. }
  896. /*!
  897. Returns the empty variable type.
  898. */
  899. function emptyVariable()
  900. {
  901. return array( "type" => "null" );
  902. }
  903. /*!
  904. \static
  905. */
  906. function mergeNamespace( $rootNamespace, $additionalNamespace )
  907. {
  908. $namespace = $rootNamespace;
  909. if ( $namespace == '' )
  910. $namespace = $additionalNamespace;
  911. else if ( $additionalNamespace != '' )
  912. $namespace = "$namespace:$additionalNamespace";
  913. return $namespace;
  914. }
  915. /*!
  916. Returns the actual value of a template type or null if an unknown type.
  917. */
  918. function elementValue( &$dataElements, $rootNamespace, $currentNamespace, $placement = false,
  919. $checkExistance = false, $checkForProxy = false )
  920. {
  921. /*
  922. * We use a small dirty hack in this function...
  923. * To help the caller to determine if the value was a proxy object,
  924. * we store boolean true to $dataElements['proxy-object-found'] in this case.
  925. * (it's up to caller to remove this garbage from $dataElements...)
  926. * This behaviour is enabled by $checkForProxy parameter.
  927. */
  928. $value = null;
  929. if ( !is_array( $dataElements ) )
  930. {
  931. $this->error( "elementValue",
  932. "Missing array data structure, got " . gettype( $dataElements ),
  933. $placement );
  934. return null;
  935. }
  936. foreach ( $dataElements as $dataElement )
  937. {
  938. if ( $dataElement === null )
  939. {
  940. return null;
  941. }
  942. $dataType = $dataElement[0];
  943. switch ( $dataType )
  944. {
  945. case eZTemplate::TYPE_VOID:
  946. {
  947. if ( !$checkExistance )
  948. $this->warning( 'elementValue',
  949. 'Found void datatype, should not be used' );
  950. else
  951. {
  952. return null;
  953. }
  954. } break;
  955. case eZTemplate::TYPE_STRING:
  956. case eZTemplate::TYPE_NUMERIC:
  957. case eZTemplate::TYPE_IDENTIFIER:
  958. case eZTemplate::TYPE_BOOLEAN:
  959. case eZTemplate::TYPE_ARRAY:
  960. {
  961. $value = $dataElement[1];
  962. } break;
  963. case eZTemplate::TYPE_VARIABLE:
  964. {
  965. $variableData = $dataElement[1];
  966. $variableNamespace = $variableData[0];
  967. $variableNamespaceScope = $variableData[1];
  968. $variableName = $variableData[2];
  969. if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_GLOBAL )
  970. $namespace = $variableNamespace;
  971. else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_LOCAL )
  972. $namespace = $this->mergeNamespace( $rootNamespace, $variableNamespace );
  973. else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_RELATIVE )
  974. $namespace = $this->mergeNamespace( $currentNamespace, $variableNamespace );
  975. else
  976. $namespace = false;
  977. if ( $this->hasVariable( $variableName, $namespace ) )
  978. {
  979. $value = $this->variable( $variableName, $namespace );
  980. }
  981. else
  982. {
  983. if ( !$checkExistance )
  984. $this->error( '', "Unknown template variable '$variableName' in namespace '$namespace'", $placement );
  985. {
  986. return null;
  987. }
  988. }
  989. } break;
  990. case eZTemplate::TYPE_ATTRIBUTE:
  991. {
  992. $attributeData = $dataElement[1];
  993. $attributeValue = $this->elementValue( $attributeData, $rootNamespace, $currentNamespace, $placement, $checkExistance );
  994. if ( $attributeValue !== null )
  995. {
  996. if ( !is_numeric( $attributeValue ) and
  997. !is_string( $attributeValue ) and
  998. !is_bool( $attributeValue ) )
  999. {
  1000. if ( !$checkExistance )
  1001. $this->error( "",
  1002. "Cannot use type " . gettype( $attributeValue ) . " for attribute lookup", $placement );
  1003. {
  1004. return null;
  1005. }
  1006. }
  1007. if ( is_array( $value ) )
  1008. {
  1009. if ( array_key_exists( $attributeValue, $value ) )
  1010. {
  1011. $value = $value[$attributeValue];
  1012. }
  1013. else
  1014. {
  1015. if ( !$checkExistance )
  1016. {
  1017. $arrayAttributeList = array_keys( $value );
  1018. $arrayCount = count( $arrayAttributeList );
  1019. $errorMessage = "No such attribute for array($arrayCount): $attributeValue";
  1020. $chooseText = "Choose one of following: ";
  1021. $errorMessage .= "\n$chooseText";
  1022. $errorMessage .= $this->expandAttributes( $arrayAttributeList, $chooseText, 25 );
  1023. $this->error( "",
  1024. $errorMessage, $placement );
  1025. }
  1026. return null;
  1027. }
  1028. }
  1029. else if ( is_object( $value ) )
  1030. {
  1031. if ( method_exists( $value, "attribute" ) and
  1032. method_exists( $value, "hasAttribute" ) )
  1033. {
  1034. if ( $value->hasAttribute( $attributeValue ) )
  1035. {
  1036. $value = $value->attribute( $attributeValue );
  1037. }
  1038. else
  1039. {
  1040. if ( !$checkExistance )
  1041. {
  1042. $objectAttributeList = array();
  1043. if ( method_exists( $value, 'attributes' ) )
  1044. $objectAttributeList = $value->attributes();
  1045. $objectClass= get_class( $value );
  1046. $errorMessage = "No such attribute for object($objectClass): $attributeValue";
  1047. $chooseText = "Choose one of following: ";
  1048. $errorMessage .= "\n$chooseText";
  1049. $errorMessage .= $this->expandAttributes( $objectAttributeList, $chooseText, 25 );
  1050. $this->error( "",
  1051. $errorMessage, $placement );
  1052. }
  1053. return null;
  1054. }
  1055. }
  1056. else
  1057. {
  1058. if ( !$checkExistance )
  1059. $this->error( "",
  1060. "Cannot retrieve attribute of object(" . get_class( $value ) .
  1061. "), no attribute functions available",
  1062. $placement );
  1063. return null;
  1064. }
  1065. }
  1066. else
  1067. {
  1068. if ( !$checkExistance )
  1069. $this->error( "",
  1070. "Cannot retrieve attribute of a " . gettype( $value ),
  1071. $placement );
  1072. return null;
  1073. }
  1074. }
  1075. else
  1076. {
  1077. if ( !$checkExistance )
  1078. $this->error( '',
  1079. 'Attribute value was null, cannot get attribute',
  1080. $placement );
  1081. return null;
  1082. }
  1083. } break;
  1084. case eZTemplate::TYPE_OPERATOR:
  1085. {
  1086. $operatorParameters = $dataElement[1];
  1087. $operatorName = $operatorParameters[0];
  1088. $operatorParameters = array_splice( $operatorParameters, 1 );
  1089. if ( is_object( $value ) and
  1090. method_exists( $value, 'templateValue' ) )
  1091. {
  1092. if ( $checkForProxy )
  1093. $dataElements['proxy-object-found'] = true;
  1094. $value = $value->templateValue();
  1095. }
  1096. $valueData = array( 'value' => $value );
  1097. $this->processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace,
  1098. $valueData, $placement, $checkExistance );
  1099. $value = $valueData['value'];
  1100. } break;
  1101. default:
  1102. {
  1103. if ( !$checkExistance )
  1104. $this->error( "elementValue",
  1105. "Unknown data type: '$dataType'",
  1106. $placement );
  1107. return null;
  1108. }
  1109. }
  1110. }
  1111. if ( is_object( $value ) and
  1112. method_exists( $value, 'templateValue' ) )
  1113. {
  1114. if ( $checkForProxy )
  1115. $dataElements['proxy-object-found'] = true;
  1116. return $value->templateValue();
  1117. }
  1118. return $value;
  1119. }
  1120. function expandAttributes( $attributeList, $chooseText, $maxThreshold, $minThreshold = 1 )
  1121. {
  1122. $errorMessage = '';
  1123. $attributeCount = count( $attributeList );
  1124. if ( $attributeCount < $minThreshold )
  1125. return $errorMessage;
  1126. if ( $attributeCount < $maxThreshold )
  1127. {
  1128. $chooseLength = strlen( $chooseText );
  1129. $attributeText = '';
  1130. $i = 0;
  1131. foreach ( $attributeList as $attributeName )
  1132. {
  1133. if ( $i > 0 )
  1134. $attributeText .= ",";
  1135. if ( strlen( $attributeText ) > 40 )
  1136. {
  1137. $attributeText .= "\n";
  1138. $errorMessage .= $attributeText;
  1139. $errorMessage .= str_repeat( ' ', $chooseLength );
  1140. $attributeText = '';
  1141. }
  1142. else if ( $i > 0 )
  1143. $attributeText .= " ";
  1144. $attributeText .= $attributeName;
  1145. ++$i;
  1146. }
  1147. $errorMessage .= $attributeText;
  1148. }
  1149. return $errorMessage;
  1150. }
  1151. function processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace,
  1152. &$valueData, $placement = false, $checkExistance = false )
  1153. {
  1154. $namedParameters = array();
  1155. $operatorParameterDefinition = $this->operatorParameterList( $operatorName );
  1156. $i = 0;
  1157. foreach ( $operatorParameterDefinition as $parameterName => $parameterType )
  1158. {
  1159. if ( !isset( $operatorParameters[$i] ) or
  1160. !isset( $operatorParameters[$i][0] ) or
  1161. $operatorParameters[$i][0] == eZTemplate::TYPE_VOID )
  1162. {
  1163. if ( $parameterType["required"] )
  1164. {
  1165. if ( !$checkExistance )
  1166. $this->warning( "eZTemplateOperatorElement", "Parameter '$parameterName' ($i) missing",
  1167. $placement );
  1168. $namedParameters[$parameterName] = $parameterType["default"];
  1169. }
  1170. else
  1171. {
  1172. $namedParameters[$parameterName] = $parameterType["default"];
  1173. }
  1174. }
  1175. else
  1176. {
  1177. $parameterData = $operatorParameters[$i];
  1178. $namedParameters[$parameterName] = $this->elementValue( $parameterData, $rootNamespace, $currentNamespace, $placement, $checkExistance );
  1179. }
  1180. ++$i;
  1181. }
  1182. if ( isset( $this->Operators[$operatorName] ) )
  1183. {
  1184. if ( is_array( $this->Operators[$operatorName] ) )
  1185. {
  1186. $this->loadAndRegisterOperators( $this->Operators[$operatorName] );
  1187. }
  1188. $op = $this->Operators[$operatorName];
  1189. if ( is_object( $op ) and method_exists( $op, 'modify' ) )
  1190. {
  1191. $value = $valueData['value'];
  1192. if ( eZTemplate::isMethodDebugEnabled() )
  1193. eZDebug::writeDebug( "START OPERATOR: $operatorName" );
  1194. $op->modify( $this, $operatorName, $operatorParameters, $rootNamespace, $currentNamespace, $value, $namedParameters,
  1195. $placement );
  1196. if ( eZTemplate::isMethodDebugEnabled() )
  1197. eZDebug::writeDebug( "END OPERATOR: $operatorName" );
  1198. $valueData['value'] = $value;
  1199. }
  1200. else
  1201. $this->error( '', "Object problem with operator '$operatorName' ",
  1202. $placement );
  1203. }
  1204. else if ( !$checkExistance )
  1205. $this->warning( "", "Operator '$operatorName' is not registered",
  1206. $placement );
  1207. }
  1208. /*!
  1209. Return the identifier used for attribute lookup.
  1210. */
  1211. function attributeValue( &$data, $nspace )
  1212. {
  1213. switch ( $data["type"] )
  1214. {
  1215. case "map":
  1216. {
  1217. return $data["content"];
  1218. } break;
  1219. case "index":
  1220. {
  1221. return $data["content"];
  1222. } break;
  1223. case "variable":
  1224. {
  1225. return $this->elementValue( $data["content"], $nspace );
  1226. } break;
  1227. default:
  1228. {
  1229. $this->error( "attributeValue()", "Unknown attribute type: " . $data["type"] );
  1230. return null;
  1231. }
  1232. }
  1233. }
  1234. /*!
  1235. Helper function for creating a displayable text for a variable.
  1236. */
  1237. function variableText( $var, $namespace = "", $attrs = array() )
  1238. {
  1239. $txt = "$";
  1240. if ( $namespace != "" )
  1241. $txt .= "$namespace:";
  1242. $txt .= $var;
  1243. if ( !empty( $attrs ) )
  1244. $txt .= "." . implode( ".", $attrs );
  1245. return $txt;
  1246. }
  1247. /*!
  1248. Returns the named parameter list for the operator $name.
  1249. */
  1250. function operatorParameterList( $name )
  1251. {
  1252. $param_list = array();
  1253. if ( !isset( $this->Operators[$name] ) )
  1254. {
  1255. return $param_list;
  1256. }
  1257. if ( is_array( $this->Operators[$name] ) )
  1258. {
  1259. $this->loadAndRegisterOperators( $this->Operators[$name] );
  1260. }
  1261. $op = $this->Operators[$name];
  1262. if ( isset( $op ) and
  1263. method_exists( $op, "namedParameterList" ) )
  1264. {
  1265. $param_list = $op->namedParameterList();
  1266. if ( method_exists( $op, "namedParameterPerOperator" ) and
  1267. $op->namedParameterPerOperator() )
  1268. {
  1269. if ( !isset( $param_list[$name] ) )
  1270. return array();
  1271. $param_list = $param_list[$name];
  1272. }
  1273. }
  1274. return $param_list;
  1275. }
  1276. /*!
  1277. Tries to run the operator $operatorName with parameters $operatorParameters
  1278. on the value $value.
  1279. */
  1280. function doOperator( $element, &$namespace, &$current_nspace, &$value, $operatorName, $operatorParameters, &$named_params )
  1281. {
  1282. if ( is_array( $this->Operators[$operatorName] ) )
  1283. {
  1284. $this->loadAndRegisterOperators( $this->Operators[$operatorName] );
  1285. }
  1286. $op = $this->Operators[$operatorName];
  1287. if ( isset( $op ) )
  1288. {
  1289. $op->modify( $element, $this, $operatorName, $operatorParameters, $namespace, $current_nspace, $value, $named_params );
  1290. }
  1291. else
  1292. $this->warning( "", "Operator \"$operatorName\" is not registered" );
  1293. }
  1294. /*!
  1295. Tries to run the function object $func_obj
  1296. */
  1297. function doFunction( $name, $func_obj, $nspace, $current_nspace )
  1298. {
  1299. $func = $this->Functions[$name];
  1300. if ( is_array( $func ) )
  1301. {
  1302. $this->loadAndRegisterFunctions( $this->Functions[$name] );
  1303. $func = $this->Functions[$name];
  1304. }
  1305. if ( isset( $func ) and
  1306. is_object( $func ) )
  1307. {
  1308. return $func->process( $this, $name, $func_obj, $nspace, $current_nspace );
  1309. }
  1310. else
  1311. {
  1312. $this->warning( "", "Function \"$name\" is not registered" );
  1313. return false;
  1314. }
  1315. }
  1316. /**
  1317. * Sets the template variable $var to the value $val.
  1318. *
  1319. * @param string $var
  1320. * @param string $val
  1321. * @param string $namespace (optional)
  1322. * @param bool $scopeSafe If true, will assure that $var is not overridden for $namespace. False by default
  1323. */
  1324. function setVariable( $var, $val, $namespace = '', $scopeSafe = false )
  1325. {
  1326. if ( $scopeSafe && isset( $this->Variables[$namespace][$var] ) )
  1327. {
  1328. $safeNamespace = $namespace;
  1329. do
  1330. {
  1331. $safeNamespace .= ':safe';
  1332. }
  1333. while( isset( $this->Variables[$safeNamespace][$var] ) );
  1334. $this->Variables[$safeNamespace][$var] = $this->Variables[$namespace][$var];
  1335. }
  1336. $this->Variables[$namespace][$var] = $val;
  1337. }
  1338. /**
  1339. * Unsets the template variable $var.
  1340. *
  1341. * @param string $var
  1342. * @param string $namespace (optional)
  1343. */
  1344. function unsetVariable( $var, $namespace = '' )
  1345. {
  1346. if ( isset( $this->Variables[$namespace] ) &&
  1347. array_key_exists( $var, $this->Variables[$namespace] ) )
  1348. {
  1349. $safeNamespace = "{$namespace}:safe";
  1350. if ( isset( $this->Variables[$safeNamespace][$var] ) )
  1351. {
  1352. // Check if a nested safe namespace for $var
  1353. // If true, then add a level of testing and test again
  1354. while( isset( $this->Variables["{$safeNamespace}:safe"][$var] ) )
  1355. {
  1356. $safeNamespace .= ':safe';
  1357. }
  1358. // Get the $var backup back and delete it
  1359. $this->Variables[$namespace][$var] = $this->Variables[$safeNamespace][$var];
  1360. unset( $this->Variables[$safeNamespace][$var] );
  1361. }
  1362. else
  1363. {
  1364. unset( $this->Variables[$namespace][$var] );
  1365. }
  1366. }
  1367. else
  1368. {
  1369. $this->warning( "unsetVariable()", "Undefined Variable: \${$namespace}:{$var}, cannot unset" );
  1370. }
  1371. }
  1372. /**
  1373. * Returns true if the variable $var is set in namespace $namespace,
  1374. * if $attrs is supplied all attributes must exist for the function to return true.
  1375. *
  1376. * @param string $var
  1377. * @param string $namespace (optional)
  1378. * @param array $attrs (optional) Deprecated as of 4.4.
  1379. * @return bool
  1380. */
  1381. function hasVariable( $var, $namespace = '', $attrs = null )
  1382. {
  1383. $exists = ( isset( $this->Variables[$namespace] ) &&
  1384. array_key_exists( $var, $this->Variables[$namespace] ) );
  1385. if ( $exists && $attrs !== null && !empty( $attrs ) )
  1386. {
  1387. eZDebug::writeStrict( '$attrs parameter is deprecated as of 4.4', __METHOD__ );
  1388. $ptr =& $this->Variables[$namespace][$var];
  1389. foreach( $attrs as $attr )
  1390. {
  1391. unset( $tmp );
  1392. if ( is_object( $ptr ) )
  1393. {
  1394. if ( $ptr->hasAttribute( $attr ) )
  1395. $tmp = $ptr->attribute( $attr );
  1396. else
  1397. return false;
  1398. }
  1399. else if ( is_array( $ptr ) )
  1400. {
  1401. if ( array_key_exists( $attr, $ptr ) )
  1402. $tmp =& $ptr[$attr];
  1403. else
  1404. return false;
  1405. }
  1406. else
  1407. {
  1408. return false;
  1409. }
  1410. unset( $ptr );
  1411. $ptr =& $tmp;
  1412. }
  1413. }
  1414. return $exists;
  1415. }
  1416. /**
  1417. * Returns the content of the variable $var using namespace $namespace,
  1418. * if $attrs is supplied the result of the attributes is returned.
  1419. *
  1420. * @param string $var
  1421. * @param string $namespace (optional)
  1422. * @param array $attrs (optional) Deprecated as of 4.4
  1423. * @return string|array
  1424. */
  1425. function variable( $var, $namespace = '', $attrs = null )
  1426. {
  1427. $val = null;
  1428. $exists = ( isset( $this->Variables[$namespace] ) &&
  1429. array_key_exists( $var, $this->Variables[$namespace] ) );
  1430. if ( $exists )
  1431. {
  1432. if ( $attrs !== null && !empty( $attrs ) )
  1433. {
  1434. eZDebug::writeStrict( '$attrs parameter is deprecated as of 4.4', __METHOD__ );
  1435. $element = $this->Variables[$namespace][$var];
  1436. foreach( $attrs as $attr )
  1437. {
  1438. if ( is_object( $element ) )
  1439. {
  1440. if ( $element->hasAttribute( $attr ) )
  1441. {
  1442. $element = $element->attribute( $attr );
  1443. }
  1444. else
  1445. {
  1446. return $val;
  1447. }
  1448. }
  1449. else if ( is_array( $element ) )
  1450. {
  1451. if ( array_key_exists( $attr, $element ) )
  1452. {
  1453. $val = $element[$attr];
  1454. }
  1455. else
  1456. {
  1457. return $val;
  1458. }
  1459. }
  1460. else
  1461. {
  1462. return $val;
  1463. }
  1464. $val = $element;
  1465. }
  1466. }
  1467. else
  1468. {
  1469. $val = $this->Variables[$namespace][$var];
  1470. }
  1471. }
  1472. return $val;
  1473. }
  1474. /*!
  1475. Returns the attribute(s) of the template variable $var,
  1476. $attrs is an array of attribute names to use iteratively for each new variable returned.
  1477. */
  1478. function variableAttribute( $var, $attrs )
  1479. {
  1480. foreach( $attrs as $attr )
  1481. {
  1482. if ( is_object( $var ) )
  1483. {
  1484. if ( $var->hasAttribute( $attr ) )
  1485. {
  1486. $var = $var->attribute( $attr );
  1487. }
  1488. else
  1489. {
  1490. return null;
  1491. }
  1492. }
  1493. else if ( is_array( $var ) )
  1494. {
  1495. if ( isset( $var[$attr] ) )
  1496. {
  1497. $var = $var[$attr];
  1498. }
  1499. else
  1500. {
  1501. return null;
  1502. }
  1503. }
  1504. else
  1505. {
  1506. return null;
  1507. }
  1508. }
  1509. if ( isset( $var ) )
  1510. {
  1511. return $var;
  1512. }
  1513. return null;
  1514. }
  1515. function appendElement( &$text, $item, $nspace, $name )
  1516. {
  1517. $this->appendElementText( $textElements, $item, $nspace, $name );
  1518. $text .= implode( '', $textElements );
  1519. }
  1520. function appendElementText( &$textElements, $item, $nspace, $name )
  1521. {
  1522. if ( !is_array( $textElements ) )
  1523. $textElements = array();
  1524. if ( is_object( $item ) and
  1525. method_exists( $item, 'templateValue' ) )
  1526. {
  1527. $item = $item->templateValue();
  1528. $textElements[] = "$item";
  1529. }
  1530. else if ( is_object( $item ) )
  1531. {
  1532. $hasTemplateData = false;
  1533. if ( method_exists( $item, 'templateData' ) )
  1534. {
  1535. $templateData = $item->templateData();
  1536. if ( is_array( $templateData ) and
  1537. isset( $templateData['type'] ) )
  1538. {
  1539. if ( $templateData['type'] == 'template' and
  1540. isset( $templateData['uri'] ) and
  1541. isset( $templateData['template_variable_name'] ) )
  1542. {
  1543. $templateURI =& $templateData['uri'];
  1544. $templateVariableName =& $templateData['template_variable_name'];
  1545. $templateText = '';
  1546. $this->setVariable( $templateVariableName, $item, $name );
  1547. eZTemplateIncludeFunction::handleInclude( $textElements, $templateURI, $this, $nspace, $name );
  1548. $hasTemplateData = true;
  1549. }
  1550. }
  1551. }
  1552. if ( !$hasTemplateData )
  1553. $textElements[] = method_exists( $item, '__toString' ) ? (string)$item : 'Object(' . get_class( $item ) . ')';
  1554. }
  1555. else
  1556. $textElements[] = "$item";
  1557. return $textElements;
  1558. }
  1559. /*!
  1560. Creates some text nodes before and after the children of \a $root.
  1561. It will extract the current filename and uri and create some XHTML
  1562. comments and inline text.
  1563. \sa isXHTMLCodeIncluded
  1564. */
  1565. function appendDebugNodes( &$root, &$resourceData )
  1566. {
  1567. $path = $resourceData['template-filename'];
  1568. // Do not ouput debug on pagelayout templates to avoid trigering
  1569. // browser quirks mode
  1570. if ( isset( $root[1][0][2] ) && is_string( $root[1][0][2] ) && stripos( $root[1][0][2], '<!DOCTYPE' ) === 0 )
  1571. return;
  1572. $uri = $resourceData['uri'];
  1573. $preText = "\n<!-- START: including template: $path ($uri) -->\n";
  1574. if ( eZTemplate::isXHTMLCodeIncluded() )
  1575. $preText .= "<p class=\"small\">$path</p><br/>\n";
  1576. $postText = "\n<!-- STOP: including template: $path ($uri) -->\n";
  1577. $preNode = eZTemplateNodeTool::createTextNode( $preText );
  1578. $postNode = eZTemplateNodeTool::createTextNode( $postText );
  1579. if ( is_array( $root[1] ) )
  1580. {
  1581. $root[1] = array_merge( array( $preNode ), $root[1] );
  1582. }
  1583. else
  1584. {
  1585. $root[1] = array( $preNode );
  1586. }
  1587. $root[1][] = $postNode;
  1588. }
  1589. /*!
  1590. Registers the functions supplied by the object $functionObject.
  1591. The object must have a function called functionList()
  1592. which returns an array of functions this object handles.
  1593. If the object has a function called attributeList()
  1594. it is used for registering function attributes.
  1595. The function returns an associative array with each key being
  1596. the name of the function and the value being a boolean.
  1597. If the boolean is true the function will have children.
  1598. */
  1599. function registerFunctions( &$functionObject )
  1600. {
  1601. $this->registerFunctionsInternal( $functionObject );
  1602. }
  1603. function registerAutoloadFunctions( $functionDefinition )
  1604. {
  1605. if ( ( ( isset( $functionDefinition['function'] ) ||
  1606. isset( $functionDefinition['class'] ) ) &&
  1607. ( isset( $functionDefinition['function_names_function'] ) ||
  1608. isset( $functionDefinition['function_names'] ) ) ) )
  1609. {
  1610. if ( isset( $functionDefinition['function_names_function'] ) )
  1611. {
  1612. $functionNamesFunction = $functionDefinition['function_names_function'];
  1613. if ( !function_exists( $functionNamesFunction ) )
  1614. {
  1615. $this->error( 'registerFunctions', "Cannot register function definition, missing function names function '$functionNamesFunction'" );
  1616. return;
  1617. }
  1618. $functionNames = $functionNamesFunction();
  1619. }
  1620. else
  1621. $functionNames = $functionDefinition['function_names'];
  1622. foreach ( $functionNames as $functionName )
  1623. {
  1624. $this->Functions[$functionName] = $functionDefinition;
  1625. }
  1626. if ( isset( $functionDefinition['function_attributes'] ) )
  1627. {
  1628. foreach ( $functionDefinition['function_attributes'] as $functionAttributeName )
  1629. {
  1630. $this->FunctionAttributes[$functionAttributeName] = $functionDefinition;
  1631. }
  1632. }
  1633. }
  1634. else
  1635. $this->error( 'registerFunctions', 'Cannot register function definition, missing data' );
  1636. }
  1637. function loadAndRegisterFunctions( $functionDefinition )
  1638. {
  1639. eZDebug::accumulatorStart( 'template_register_function', 'template_total', 'Template load and register function' );
  1640. $functionObject = null;
  1641. if ( isset( $functionDefinition['function'] ) )
  1642. {
  1643. $function = $functionDefinition['function'];
  1644. if ( function_exists( $function ) )
  1645. $functionObject = $function();
  1646. }
  1647. else
  1648. {
  1649. if ( !class_exists( $functionDefinition['class'], false )
  1650. && isset( $functionDefinition['script'] ) )
  1651. {
  1652. include_once( $functionDefinition['script'] );
  1653. }
  1654. $class = $functionDefinition['class'];
  1655. if ( class_exists( $class ) )
  1656. $functionObject = new $class();
  1657. }
  1658. eZDebug::accumulatorStop( 'template_register_function' );
  1659. if ( is_object( $functionObject ) )
  1660. {
  1661. $this->registerFunctionsInternal( $functionObject, true );
  1662. return true;
  1663. }
  1664. return false;
  1665. }
  1666. /*!
  1667. \private
  1668. */
  1669. function registerFunctionsInternal( $functionObject, $debug = false )
  1670. {
  1671. if ( !is_object( $functionObject ) or
  1672. !method_exists( $functionObject, 'functionList' ) )
  1673. return false;
  1674. foreach ( $functionObject->functionList() as $functionName )
  1675. {
  1676. $this->Functions[$functionName] = $functionObject;
  1677. }
  1678. if ( method_exists( $functionObject, "attributeList" ) )
  1679. {
  1680. $functionAttributes = $functionObject->attributeList();
  1681. foreach ( $functionAttributes as $attributeName => $hasChildren )
  1682. {
  1683. $this->FunctionAttributes[$attributeName] = $hasChildren;
  1684. }
  1685. }
  1686. return true;
  1687. }
  1688. /*!
  1689. Registers the function $func_name to be bound to object $func_obj.
  1690. If the object has a function called attributeList()
  1691. it is used for registering function attributes.
  1692. The function returns an associative array with each key being
  1693. the name of the function and the value being a boolean.
  1694. If the boolean is true the function will have children.
  1695. */
  1696. function registerFunction( $func_name, $func_obj )
  1697. {
  1698. $this->Functions[$func_name] = $func_obj;
  1699. if ( method_exists( $func_obj, "attributeList" ) )
  1700. {
  1701. $attrs = $func_obj->attributeList();
  1702. foreach ( $attrs as $attr_name => $has_children )
  1703. {
  1704. $this->FunctionAttributes[$attr_name] = $has_children;
  1705. }
  1706. }
  1707. }
  1708. /*!
  1709. Registers a new literal tag in which the tag will be transformed into
  1710. a text element.
  1711. */
  1712. function registerLiteral( $func_name )
  1713. {
  1714. $this->Literals[$func_name] = true;
  1715. }
  1716. /*!
  1717. Removes the literal tag $func_name.
  1718. */
  1719. function unregisterLiteral( $func_name )
  1720. {
  1721. unset( $this->Literals[$func_name] );
  1722. }
  1723. function registerAutoloadOperators( $operatorDefinition )
  1724. {
  1725. if ( ( ( isset( $operatorDefinition['function'] ) ||
  1726. isset( $operatorDefinition['class'] ) ) &&
  1727. ( isset( $operatorDefinition['operator_names_function'] ) ||
  1728. isset( $operatorDefinition['operator_names'] ) ) ) )
  1729. {
  1730. if ( isset( $operatorDefinition['operator_names_function'] ) )
  1731. {
  1732. $operatorNamesFunction = $operatorDefinition['operator_names_function'];
  1733. if ( !function_exists( $operatorNamesFunction ) )
  1734. {
  1735. $this->error( 'registerOperators', "Cannot register operator definition, missing operator names function '$operatorNamesFunction'" );
  1736. return;
  1737. }
  1738. $operatorNames = $operatorNamesFunction();
  1739. }
  1740. else
  1741. $operatorNames = $operatorDefinition['operator_names'];
  1742. foreach ( $operatorNames as $operatorName )
  1743. {
  1744. $this->Operators[$operatorName] = $operatorDefinition;
  1745. }
  1746. }
  1747. else
  1748. $this->error( 'registerOperators', 'Cannot register operator definition, missing data' );
  1749. }
  1750. function loadAndRegisterOperators( $operatorDefinition )
  1751. {
  1752. $operatorObject = null;
  1753. if ( isset( $operatorDefinition['function'] ) )
  1754. {
  1755. $function = $operatorDefinition['function'];
  1756. if ( function_exists( $function ) )
  1757. $operatorObject = $function();
  1758. }
  1759. else
  1760. {
  1761. $class = $operatorDefinition['class'];
  1762. if ( !class_exists( $class, false ) && isset( $operatorDefinition['script'] ) )
  1763. {
  1764. include_once( $operatorDefinition['script'] );
  1765. }
  1766. if ( class_exists( $class ) )
  1767. {
  1768. if ( isset( $operatorDefinition['class_parameter'] ) )
  1769. $operatorObject = new $class( $operatorDefinition['class_parameter'] );
  1770. else
  1771. $operatorObject = new $class();
  1772. }
  1773. }
  1774. if ( is_object( $operatorObject ) )
  1775. {
  1776. $this->registerOperatorsInternal( $operatorObject, true );
  1777. return true;
  1778. }
  1779. return false;
  1780. }
  1781. /*!
  1782. Registers the operators supplied by the object $operatorObject.
  1783. The function operatorList() must return an array of operator names.
  1784. */
  1785. function registerOperators( &$operatorObject )
  1786. {
  1787. $this->registerOperatorsInternal( $operatorObject );
  1788. }
  1789. function registerOperatorsInternal( $operatorObject, $debug = false )
  1790. {
  1791. if ( !is_object( $operatorObject ) or
  1792. !method_exists( $operatorObject, 'operatorList' ) )
  1793. return false;
  1794. foreach( $operatorObject->operatorList() as $operatorName )
  1795. {
  1796. $this->Operators[$operatorName] = $operatorObject;
  1797. }
  1798. }
  1799. /*!
  1800. Registers the operator $op_name to use the object $op_obj.
  1801. */
  1802. function registerOperator( $op_name, $op_obj )
  1803. {
  1804. $this->Operators[$op_name] = $op_obj;
  1805. }
  1806. /*!
  1807. Unregisters the operator $op_name.
  1808. */
  1809. function unregisterOperator( $op_name )
  1810. {
  1811. if ( is_array( $op_name ) )
  1812. {
  1813. foreach ( $op_name as $op )
  1814. {
  1815. $this->unregisterOperator( $op_name );
  1816. }
  1817. }
  1818. else if ( isset( $this->Operators ) )
  1819. unset( $this->Operators[$op_name] );
  1820. else
  1821. $this->warning( "unregisterOpearator()", "Operator $op_name is not registered, cannot unregister" );
  1822. }
  1823. /*!
  1824. Not implemented yet.
  1825. */
  1826. function registerFilter()
  1827. {
  1828. }
  1829. /*!
  1830. Registers a new resource object $res.
  1831. The resource object take care of fetching templates using an URI.
  1832. */
  1833. function registerResource( $res )
  1834. {
  1835. if ( is_object( $res ) )
  1836. $this->Resources[$res->resourceName()] =& $res;
  1837. else
  1838. $this->warning( "registerResource()", "Supplied argument is not a resource object" );
  1839. }
  1840. /*!
  1841. Unregisters the resource $res_name.
  1842. */
  1843. function unregisterResource( $res_name )
  1844. {
  1845. if ( is_array( $res_name ) )
  1846. {
  1847. foreach ( $res_name as $res )
  1848. {
  1849. $this->unregisterResource( $res );
  1850. }
  1851. }
  1852. else if ( isset( $this->Resources[$res_name] ) )
  1853. unset( $this->Resources[$res_name] );
  1854. else
  1855. $this->warning( "unregisterResource()", "Resource $res_name is not registered, cannot unregister" );
  1856. }
  1857. /*!
  1858. Sets whether detail output is used or not.
  1859. Detail output is useful for debug output where you want to examine the template
  1860. and the output text.
  1861. */
  1862. function setShowDetails( $show )
  1863. {
  1864. $this->ShowDetails = $show;
  1865. }
  1866. /*!
  1867. Outputs a warning about the parameter $param missing for function/operator $name.
  1868. */
  1869. function missingParameter( $name, $param )
  1870. {
  1871. $this->warning( $name, "Missing parameter $param" );
  1872. }
  1873. /*!
  1874. Outputs a warning about the parameter count being to high for function/operator $name.
  1875. */
  1876. function extraParameters( $name, $count, $maxCount )
  1877. {
  1878. $this->warning( $name, "Passed $count parameters but correct count is $maxCount" );
  1879. }
  1880. /*!
  1881. Outputs a warning about the variable $var being undefined.
  1882. */
  1883. function undefinedVariable( $name, $var )
  1884. {
  1885. $this->warning( $name, "Undefined variable: $var" );
  1886. }
  1887. /*!
  1888. Outputs an error about the template function $func_name being undefined.
  1889. */
  1890. function undefinedFunction( $func_name )
  1891. {
  1892. $this->error( "", "Undefined function: $func_name" );
  1893. }
  1894. /*!
  1895. Creates a string for the placement information and returns it.
  1896. \note The placement information can either be in indexed or associative
  1897. */
  1898. function placementText( $placement = false )
  1899. {
  1900. $placementText = false;
  1901. if ( $placement !== false )
  1902. {
  1903. if ( isset( $placement['start'] ) and
  1904. isset( $placement['stop'] ) and
  1905. isset( $placement['templatefile'] ) )
  1906. {
  1907. $line = $placement['start']['line'];
  1908. $column = $placement['start']['column'];
  1909. $templateFile = $placement['templatefile'];
  1910. }
  1911. else
  1912. {
  1913. $line = $placement[0][0];
  1914. $column = $placement[0][1];
  1915. $templateFile = $placement[2];
  1916. }
  1917. $placementText = " @ $templateFile:$line" . "[$column]";
  1918. }
  1919. return $placementText;
  1920. }
  1921. /*!
  1922. Displays a warning for the function/operator $name and text $txt.
  1923. */
  1924. function warning( $name, $txt, $placement = false )
  1925. {
  1926. $this->WarningLog[] = array( 'name' => $name,
  1927. 'text' => $txt,
  1928. 'placement' => $placement );
  1929. if ( !is_string( $placement ) )
  1930. $placementText = $this->placementText( $placement );
  1931. else
  1932. $placementText = $placement;
  1933. $placementText = $this->placementText( $placement );
  1934. if ( $name != "" )
  1935. eZDebug::writeWarning( $txt, "eZTemplate:$name" . $placementText );
  1936. else
  1937. eZDebug::writeWarning( $txt, "eZTemplate" . $placementText );
  1938. }
  1939. /*!
  1940. Displays an error for the function/operator $name and text $txt.
  1941. */
  1942. function error( $name, $txt, $placement = false )
  1943. {
  1944. $this->ErrorLog[] = array( 'name' => $name,
  1945. 'text' => $txt,
  1946. 'placement' => $placement );
  1947. if ( !is_string( $placement ) )
  1948. $placementText = $this->placementText( $placement );
  1949. else
  1950. $placementText = $placement;
  1951. if ( $name != "" )
  1952. $nameText = "eZTemplate:$name";
  1953. else
  1954. $nameText = "eZTemplate";
  1955. eZDebug::writeError( $txt, $nameText . $placementText );
  1956. $hasAppendWarning =& $GLOBALS['eZTemplateHasAppendWarning'];
  1957. $ini = $this->ini();
  1958. if ( $ini->variable( 'ControlSettings', 'DisplayWarnings' ) == 'enabled' )
  1959. {
  1960. if ( !isset( $hasAppendWarning ) or
  1961. !$hasAppendWarning )
  1962. {
  1963. if ( function_exists( 'eZAppendWarningItem' ) )
  1964. {
  1965. eZAppendWarningItem( array( 'error' => array( 'type' => 'template',
  1966. 'number' => eZTemplate::FILE_ERRORS ),
  1967. 'text' => ezpI18n::tr( 'lib/eztemplate', 'Some template errors occurred, see debug for more information.' ) ) );
  1968. $hasAppendWarning = true;
  1969. }
  1970. }
  1971. }
  1972. }
  1973. function operatorInputSupported( $operatorName )
  1974. {
  1975. }
  1976. /*!
  1977. Sets the original text for uri $uri to $text.
  1978. */
  1979. function setIncludeText( $uri, $text )
  1980. {
  1981. $this->IncludeText[$uri] = $text;
  1982. }
  1983. /*!
  1984. Sets the output for uri $uri to $output.
  1985. */
  1986. function setIncludeOutput( $uri, $output )
  1987. {
  1988. $this->IncludeOutput[$uri] = $output;
  1989. }
  1990. /*!
  1991. \return the path list which is used for autoloading functions and operators.
  1992. */
  1993. function autoloadPathList()
  1994. {
  1995. return $this->AutoloadPathList;
  1996. }
  1997. /*!
  1998. Sets the path list for autoloading.
  1999. */
  2000. function setAutoloadPathList( $pathList )
  2001. {
  2002. $this->AutoloadPathList = $pathList;
  2003. }
  2004. /*!
  2005. Looks trough the pathes specified in autoloadPathList() and fetches autoload
  2006. definition files used for autoloading functions and operators.
  2007. */
  2008. function autoload()
  2009. {
  2010. $pathList = $this->autoloadPathList();
  2011. foreach ( $pathList as $path )
  2012. {
  2013. $autoloadFile = $path . 'eztemplateautoload.php';
  2014. if ( file_exists( $autoloadFile ) )
  2015. {
  2016. unset( $eZTemplateOperatorArray );
  2017. unset( $eZTemplateFunctionArray );
  2018. include( $autoloadFile );
  2019. if ( isset( $eZTemplateOperatorArray ) &&
  2020. is_array( $eZTemplateOperatorArray ) )
  2021. {
  2022. foreach ( $eZTemplateOperatorArray as $operatorDefinition )
  2023. {
  2024. $this->registerAutoloadOperators( $operatorDefinition );
  2025. }
  2026. }
  2027. if ( isset( $eZTemplateFunctionArray ) &&
  2028. is_array( $eZTemplateFunctionArray ) )
  2029. {
  2030. foreach ( $eZTemplateFunctionArray as $functionDefinition )
  2031. {
  2032. $this->registerAutoloadFunctions( $functionDefinition );
  2033. }
  2034. }
  2035. }
  2036. else
  2037. {
  2038. eZDebug::writeWarning( "Path '$path' does not have the file 'eztemplateautoload.php' allthough it reported it had one.\n" .
  2039. "Looked for file '" . $autoloadFile . "'\n" .
  2040. "Check the setting [TemplateSettings]/ExtensionAutoloadPath or AutoloadPathList in your site.ini settings." );
  2041. }
  2042. }
  2043. }
  2044. /*!
  2045. Resets all template variables.
  2046. */
  2047. function resetVariables()
  2048. {
  2049. $this->Variables = array();
  2050. }
  2051. /*!
  2052. Resets all template functions and operators by calling the resetFunction and resetOperator
  2053. on all elements that supports it.
  2054. */
  2055. function resetElements()
  2056. {
  2057. foreach ( $this->Functions as $functionName => $functionObject )
  2058. {
  2059. if ( is_object( $functionObject ) and
  2060. method_exists( $functionObject, 'resetFunction' ) )
  2061. {
  2062. $functionObject->resetFunction( $functionName );
  2063. }
  2064. }
  2065. foreach ( $this->Operators as $operatorName => $operatorObject )
  2066. {
  2067. if ( is_object( $operatorObject ) and
  2068. method_exists( $operatorObject, 'resetOperator' ) )
  2069. {
  2070. $operatorObject->resetOperator( $operatorName );
  2071. }
  2072. }
  2073. }
  2074. /*!
  2075. Resets all template variables, functions, operators and error counts.
  2076. */
  2077. function reset()
  2078. {
  2079. $this->resetVariables();
  2080. $this->resetElements();
  2081. $this->IsCachingAllowed = true;
  2082. $this->resetErrorLog();
  2083. $this->TemplatesUsageStatistics = array();
  2084. $this->TemplateFetchList = array();
  2085. }
  2086. /*!
  2087. \return The number of errors that occured with the last fetch
  2088. \sa hasErrors()
  2089. */
  2090. function errorCount()
  2091. {
  2092. return count( $this->ErrorLog );
  2093. }
  2094. /*!
  2095. \return \ true if errors occured with the last fetch.
  2096. \sa errorCount()
  2097. */
  2098. function hasErrors()
  2099. {
  2100. return $this->errorCount() > 0;
  2101. }
  2102. /*!
  2103. \return error log.
  2104. \sa errorCount()
  2105. */
  2106. function errorLog()
  2107. {
  2108. return $this->ErrorLog;
  2109. }
  2110. /*!
  2111. \return The number of warnings that occured with the last fetch
  2112. \sa hasWarnings()
  2113. */
  2114. function warningCount()
  2115. {
  2116. return count( $this->WarningLog );
  2117. }
  2118. /*!
  2119. \return \ true if warnings occured with the last fetch.
  2120. \sa warningCount()
  2121. */
  2122. function hasWarnings()
  2123. {
  2124. return $this->warningCount() > 0;
  2125. }
  2126. /*!
  2127. \return waring log.
  2128. \sa warningCount()
  2129. */
  2130. function warningLog()
  2131. {
  2132. return $this->WarningLog;
  2133. }
  2134. /**
  2135. * Returns a shared instance of the eZTemplate class.
  2136. *
  2137. * @return eZTemplate
  2138. */
  2139. public static function instance()
  2140. {
  2141. if ( self::$instance === null )
  2142. {
  2143. self::$instance = new eZTemplate();
  2144. }
  2145. return self::$instance;
  2146. }
  2147. /**
  2148. * Returns a shared instance of the eZTemplate class with
  2149. * default settings applied, like:
  2150. * - Autoload operators loaded
  2151. * - Debug mode set
  2152. * - eZTemplateDesignResource::instance registered
  2153. *
  2154. * @since 4.3
  2155. * @return eZTemplate
  2156. */
  2157. public static function factory()
  2158. {
  2159. if ( self::$factory === false )
  2160. {
  2161. $instance = self::instance();
  2162. $ini = eZINI::instance();
  2163. if (!isset($GLOBALS['eZTemplateDebugInternalsEnabled']) && $ini->variable( 'TemplateSettings', 'Debug' ) == 'enabled' )
  2164. eZTemplate::setIsDebugEnabled( true );
  2165. $instance->setAutoloadPathList(
  2166. array_unique(
  2167. array_merge(
  2168. $ini->variable( 'TemplateSettings', 'AutoloadPathList' ),
  2169. eZExtension::expandedPathList(
  2170. $ini->variable( 'TemplateSettings', 'ExtensionAutoloadPath' ),
  2171. 'autoloads/'
  2172. )
  2173. )
  2174. )
  2175. );
  2176. $instance->autoload();
  2177. $instance->registerResource( eZTemplateDesignResource::instance() );
  2178. self::$factory = true;
  2179. }
  2180. return self::instance();
  2181. }
  2182. /**
  2183. * Reset shared instance of the eZTemplate class and factory flag
  2184. * as used by {@link eZTemplate::instance()} and {@link eZTemplate::factory()}
  2185. *
  2186. * @since 4.3
  2187. */
  2188. public static function resetInstance()
  2189. {
  2190. self::$instance = null;
  2191. self::$factory = false;
  2192. }
  2193. /**
  2194. * Returns the eZINI object instance for the template.ini file.
  2195. *
  2196. * @return eZINI
  2197. */
  2198. public function ini()
  2199. {
  2200. return eZINI::instance( "template.ini" );
  2201. }
  2202. /*!
  2203. \static
  2204. \return true if special XHTML code should be included before the included template file.
  2205. This code will display the template filename in the browser but will eventually
  2206. break the design.
  2207. */
  2208. static function isXHTMLCodeIncluded()
  2209. {
  2210. if ( !isset( $GLOBALS['eZTemplateDebugXHTMLCodeEnabled'] ) )
  2211. {
  2212. $ini = eZINI::instance();
  2213. $GLOBALS['eZTemplateDebugXHTMLCodeEnabled'] = $ini->variable( 'TemplateSettings', 'ShowXHTMLCode' ) == 'enabled';
  2214. }
  2215. return $GLOBALS['eZTemplateDebugXHTMLCodeEnabled'];
  2216. }
  2217. /*!
  2218. \static
  2219. \return \c true if debug output of template functions and operators should be enabled.
  2220. */
  2221. static function isMethodDebugEnabled()
  2222. {
  2223. if ( !isset( $GLOBALS['eZTemplateDebugMethodEnabled'] ) )
  2224. {
  2225. $ini = eZINI::instance();
  2226. $GLOBALS['eZTemplateDebugMethodEnabled'] = $ini->variable( 'TemplateSettings', 'ShowMethodDebug' ) == 'enabled';
  2227. }
  2228. return $GLOBALS['eZTemplateDebugMethodEnabled'];
  2229. }
  2230. /*!
  2231. \static
  2232. \return true if debugging of internals is enabled, this will display
  2233. which files are loaded and when cache files are created.
  2234. Set the option with setIsDebugEnabled().
  2235. */
  2236. static function isDebugEnabled()
  2237. {
  2238. if ( !isset( $GLOBALS['eZTemplateDebugInternalsEnabled'] ) )
  2239. $GLOBALS['eZTemplateDebugInternalsEnabled'] = eZTemplate::DEBUG_INTERNALS;
  2240. return $GLOBALS['eZTemplateDebugInternalsEnabled'];
  2241. }
  2242. /*!
  2243. \static
  2244. Sets whether internal debugging is enabled or not.
  2245. */
  2246. static function setIsDebugEnabled( $debug )
  2247. {
  2248. $GLOBALS['eZTemplateDebugInternalsEnabled'] = $debug;
  2249. }
  2250. /*!
  2251. \return \c true if caching is allowed (default) or \c false otherwise.
  2252. This also affects template compiling.
  2253. \sa setIsCachingAllowed
  2254. */
  2255. function isCachingAllowed()
  2256. {
  2257. return $this->IsCachingAllowed;
  2258. }
  2259. /*!
  2260. Sets whether caching/compiling is allowed or not. This is useful
  2261. if you need to make sure templates are parsed and processed
  2262. without any caching mechanisms.
  2263. \note The default is to allow caching.
  2264. \sa isCachingAllowed
  2265. */
  2266. function setIsCachingAllowed( $allowed )
  2267. {
  2268. $this->IsCachingAllowed = $allowed;
  2269. }
  2270. /*!
  2271. \static
  2272. \return \c true if templates usage statistics should be enabled.
  2273. */
  2274. static function isTemplatesUsageStatisticsEnabled()
  2275. {
  2276. if ( !isset( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] ) )
  2277. {
  2278. $ini = eZINI::instance();
  2279. $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] = $ini->variable( 'TemplateSettings', 'ShowUsedTemplates' ) == 'enabled';
  2280. }
  2281. return ( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] );
  2282. }
  2283. /*!
  2284. \static
  2285. Sets whether templates usage statistics enabled or not.
  2286. \return \c true if templates usage statistics was enabled, otherwise \c false.
  2287. */
  2288. function setIsTemplatesUsageStatisticsEnabled( $enabled )
  2289. {
  2290. $wasEnabled = false;
  2291. if( isset( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] ) )
  2292. $wasEnabled = $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'];
  2293. $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] = $enabled;
  2294. return $wasEnabled;
  2295. }
  2296. /*!
  2297. \static
  2298. Checks settings and if 'ShowUsedTemplates' is enabled appends template info to stats.
  2299. */
  2300. function appendTemplateToStatisticsIfNeeded( &$templateName, &$templateFileName )
  2301. {
  2302. if ( eZTemplate::isTemplatesUsageStatisticsEnabled() )
  2303. eZTemplate::appendTemplateToStatistics( $templateName, $templateFileName );
  2304. }
  2305. /*!
  2306. \static
  2307. Appends template info to stats.
  2308. */
  2309. function appendTemplateToStatistics( $templateName, $templateFileName )
  2310. {
  2311. $actualTemplateName = preg_replace( "#^[\w/]+templates/#", '', $templateFileName );
  2312. $requestedTemplateName = preg_replace( "#^[\w/]+templates/#", '', $templateName );
  2313. $tpl = eZTemplate::instance();
  2314. $needToAppend = true;
  2315. // don't add template info if it is a duplicate of previous.
  2316. $statsSize = count( $tpl->TemplatesUsageStatistics );
  2317. if ( $statsSize > 0 )
  2318. {
  2319. $lastTemplateInfo = $tpl->TemplatesUsageStatistics[$statsSize-1];
  2320. if ( $lastTemplateInfo['actual-template-name'] === $actualTemplateName &&
  2321. $lastTemplateInfo['requested-template-name'] === $requestedTemplateName &&
  2322. $lastTemplateInfo['template-filename'] === $templateFileName )
  2323. {
  2324. $needToAppend = false;
  2325. }
  2326. }
  2327. if ( $needToAppend )
  2328. {
  2329. $templateInfo = array( 'actual-template-name' => $actualTemplateName,
  2330. 'requested-template-name' => $requestedTemplateName,
  2331. 'template-filename' => $templateFileName );
  2332. $tpl->TemplatesUsageStatistics[] = $templateInfo;
  2333. }
  2334. }
  2335. /*!
  2336. Appends template info for current fetch.
  2337. */
  2338. function appendTemplateFetch( $actualTemplateName )
  2339. {
  2340. $this->TemplateFetchList[] = $actualTemplateName;
  2341. $this->TemplateFetchList = array_unique( $this->TemplateFetchList );
  2342. }
  2343. /*!
  2344. Reset error and warning logs
  2345. */
  2346. function resetErrorLog()
  2347. {
  2348. $this->ErrorLog = array();
  2349. $this->WarningLog = array();
  2350. }
  2351. /*!
  2352. \static
  2353. Returns template usage statistics
  2354. */
  2355. static function templatesUsageStatistics()
  2356. {
  2357. $tpl = eZTemplate::instance();
  2358. return $tpl->TemplatesUsageStatistics;
  2359. }
  2360. /*!
  2361. Returns template list for the last fetch.
  2362. */
  2363. function templateFetchList()
  2364. {
  2365. return $this->TemplateFetchList;
  2366. }
  2367. /*!
  2368. Set template compilation test mode.
  2369. \param true, will set template compilation in test mode ( no disc writes ).
  2370. false, will compile templates to disc
  2371. */
  2372. function setCompileTest( $val )
  2373. {
  2374. $this->TestCompile = $val;
  2375. }
  2376. /*!
  2377. Get if template session is test compile
  2378. */
  2379. function testCompile()
  2380. {
  2381. return $this->TestCompile;
  2382. }
  2383. /// \privatesection
  2384. /// Associative array of resource objects
  2385. public $Resources;
  2386. /// Reference to the default resource object
  2387. public $DefaultResource;
  2388. /// The original template text
  2389. public $Text;
  2390. /// Included texts, usually performed by custom functions
  2391. public $IncludeText;
  2392. /// Included outputs, usually performed by custom functions
  2393. public $IncludeOutput;
  2394. /// The timestamp of the template when it was last modified
  2395. public $TimeStamp;
  2396. /// The left delimiter used for parsing
  2397. public $LDelim;
  2398. /// The right delimiter used for parsing
  2399. public $RDelim;
  2400. /// The resulting object tree of the template
  2401. public $Tree;
  2402. /// An associative array of template variables
  2403. public $Variables;
  2404. /*!
  2405. Last element of this stack contains names of
  2406. all variables created in the innermost template, for them
  2407. to be destroyed after the template execution finishes.
  2408. */
  2409. public $LocalVariablesNamesStack;
  2410. // Reference to the last element of $LocalVariablesNamesStack.
  2411. public $CurrentLocalVariablesNames;
  2412. /// An associative array of operators
  2413. public $Operators;
  2414. /// An associative array of functions
  2415. public $Functions;
  2416. /// An associative array of function attributes
  2417. public $FunctionAttributes;
  2418. /// An associative array of literal tags
  2419. public $Literals;
  2420. /// True if output details is to be shown
  2421. public $ShowDetails = false;
  2422. /// \c true if caching is allowed
  2423. public $IsCachingAllowed;
  2424. /// Array containing all errors occured during a fetch
  2425. public $ErrorLog;
  2426. /// Array containing all warnings occured during a fetch
  2427. public $WarningLog;
  2428. public $AutoloadPathList;
  2429. /// include level
  2430. public $Level = 0;
  2431. public $MaxLevel = 40;
  2432. /// A list of templates used by a rendered page
  2433. public $TemplatesUsageStatistics;
  2434. // counter to make unique names for {foreach} loop variables in com
  2435. public $ForeachCounter;
  2436. public $ForCounter;
  2437. public $WhileCounter;
  2438. public $DoCounter;
  2439. public $ElseifCounter;
  2440. // Flag for setting compilation in test mode
  2441. public $TestCompile;
  2442. /**
  2443. * Singelton instance of eZTemplate used by {@link eZTemplate::instance()}
  2444. * Reset with {@link eZTemplate::resetInstance()}
  2445. *
  2446. * @var null|eZTemplate
  2447. */
  2448. protected static $instance;
  2449. /**
  2450. * Factory flag as used by {@link eZTemplate::factory()}
  2451. * Reset with {@link eZTemplate::resetInstance()}
  2452. *
  2453. * @var bool
  2454. */
  2455. protected static $factory = false;
  2456. // public $CurrentRelatedResource;
  2457. // public $CurrentRelatedTemplateName;
  2458. }
  2459. ?>