|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing the eZTemplate class. 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package lib 00009 */ 00010 00011 /*! \defgroup eZTemplate Template system */ 00012 00013 /*! 00014 \class eZTemplate eztemplate.php 00015 \ingroup eZTemplate 00016 \brief The main manager for templates 00017 00018 The template systems allows for separation of code and 00019 layout by moving the layout part into template files. These 00020 template files are parsed and processed with template variables set 00021 by the PHP code. 00022 00023 The template system in itself is does not do much, it parses template files 00024 according to a rule set sets up a tree hierarchy and process the data 00025 using functions and operators. The standard template system comes with only 00026 a few functions and no operators, it is meant for these functions and operators 00027 to be specified by the users of the template system. But for simplicity a few 00028 help classes is available which can be easily enabled. 00029 00030 The classes are: 00031 - eZTemplateDelimitFunction - Inserts the left and right delimiter which are normally parsed. 00032 - eZTemplateSectionFunction - Allows for conditional blocks and loops. 00033 - eZTemplateIncludeFunction - Includes external templates 00034 - eZTemplateSequenceFunction - Creates sequences arrays 00035 - eZTemplateSwitchFunction - Conditional output of template 00036 00037 - eZTemplatePHPOperator - Allows for easy redirection of operator names to PHP functions. 00038 - eZTemplateLocaleOperator - Allows for locale conversions. 00039 - eZTemplateArrayOperator - Creates arrays 00040 - eZTemplateAttributeOperator - Displays contents of template variables, useful for debugging 00041 - eZTemplateImageOperator - Converts text to image 00042 - eZTemplateLogicOperator - Various logical operators for boolean handling 00043 - eZTemplateUnitOperator - Unit conversion and display 00044 00045 To enable these functions and operator use registerFunction and registerOperator. 00046 00047 In keeping with the spirit of being simple the template system does not know how 00048 to get the template files itself. Instead it relies on resource handlers, these 00049 handlers fetches the template files using different kind of transport mechanism. 00050 For simplicity a default resource class is available, eZTemplateFileResource fetches 00051 templates from the filesystem. 00052 00053 The parser process consists of three passes, each pass adds a new level of complexity. 00054 The first pass strips text from template blocks which starts with a left delimiter and 00055 ends with a right delimiter (default is { and } ), and places them in an array. 00056 The second pass iterates the text and block elements and removes newlines from 00057 text before function blocks and text after function blocks. 00058 The third pass builds the tree according the function rules. 00059 00060 Processing is done by iterating over the root of the tree, if a text block is found 00061 the text is appended to the result text. If a variable or contant is it's data is extracted 00062 and any operators found are run on it before fetching the result and appending it to 00063 the result text. If a function is found the function is called with the parameters 00064 and it's up to the function handle children if any. 00065 00066 Constants and template variables will usually be called variables since there's little 00067 difference. A template variable expression will start with a $ and consists of a 00068 namespace (optional) a name and attribues(optional). The variable expression 00069 \verbatim $root:var.attr1 \endverbatim exists in the "root" namespace, has the name "var" and uses the 00070 attribute "attr1". Some functions will create variables on demand, to avoid name conflicts 00071 namespaces were introduced, each function will place the new variables in a namespace 00072 specified in the template file. Attribues are used for fetching parts of the variable, 00073 for instance an element in an array or data in an object. Since the syntax is the 00074 same for arrays and objects the PHP code can use simple arrays when speed is required, 00075 the template code will not care. 00076 A different syntax is also available when you want to access an attribute using a variable. 00077 For instance \verbatim $root:var[$attr_var] \endverbatim, if the variable $attr_var contains "attr1" it would 00078 access the same attribute as in the first example. 00079 00080 The syntax for operators is a | and a name, optionally parameters can be specified with 00081 ( and ) delimited with ,. Valid operators are \verbatim |upcase, |l10n(date) \endverbatim. 00082 00083 Functions look a lot like HTML/XML tags. The function consists of a name and parameters 00084 which are assigned using the param=value syntax. Some parameters may be required while 00085 others may be optionally, the exact behaviour is specified by each function. 00086 Valid functions are \verbatim "section name=abc loop=4" \endverbatim 00087 00088 Example of usage: 00089 \code 00090 // Init template 00091 $tpl = eZTemplate::instance(); 00092 00093 $tpl->registerOperators( new eZTemplatePHPOperator( array( "upcase" => "strtoupper", 00094 "reverse" => "strrev" ) ) ); 00095 $tpl->registerOperators( new eZTemplateLocaleOperator() ); 00096 $tpl->registerFunction( "section", new eZTemplateSectionFunction( "section" ) ); 00097 $tpl->registerFunctions( new eZTemplateDelimitFunction() ); 00098 00099 $tpl->setVariable( "my_var", "{this value set by variable}", "test" ); 00100 $tpl->setVariable( "my_arr", array( "1st", "2nd", "third", "fjerde" ) ); 00101 $tpl->setVariable( "multidim", array( array( "a", "b" ), 00102 array( "c", "d" ), 00103 array( "e", "f" ), 00104 array( "g", "h" ) ) ); 00105 00106 class mytest 00107 { 00108 function mytest( $n, $s ) 00109 { 00110 $this->n = $n; 00111 $this->s = $s; 00112 } 00113 00114 function hasAttribute( $attr ) 00115 { 00116 return ( $attr == "name" || $attr == "size" ); 00117 } 00118 00119 function attribute( $attr ) 00120 { 00121 switch ( $attr ) 00122 { 00123 case "name"; 00124 return $this->n; 00125 case "size"; 00126 return $this->s; 00127 default: 00128 $retAttr = null; 00129 return $retAttr; 00130 } 00131 } 00132 00133 } 00134 00135 $tpl->setVariable( "multidim_obj", array( new mytest( "jan", 200 ), 00136 new mytest( "feb", 200 ), 00137 new mytest( "john", 200 ), 00138 new mytest( "doe", 50 ) ) ); 00139 $tpl->setVariable( "curdate", time() ); 00140 00141 $tpl->display( "lib/eztemplate/example/test.tpl" ); 00142 00143 // test.tpl 00144 00145 {section name=outer loop=4} 00146 123 00147 {delimit}::{/delimit} 00148 {/section} 00149 00150 {literal test=1} This is some {blah arg1="" arg2="abc" /} {/literal} 00151 00152 <title>This is a test</title> 00153 <table border="1"> 00154 <tr><th>{$test:my_var} 00155 {"some text!!!"|upcase|reverse}</th></tr> 00156 {section name=abc loop=$my_arr} 00157 <tr><td>{$abc:item}</td></tr> 00158 {/section} 00159 </table> 00160 00161 <table border="1"> 00162 {section name=outer loop=$multidim} 00163 <tr> 00164 {section name=inner loop=$outer:item} 00165 <td>{$inner:item}</td> 00166 {/section} 00167 </tr> 00168 {/section} 00169 </table> 00170 00171 <table border="1"> 00172 {section name=outer loop=$multidim_obj} 00173 <tr> 00174 <td>{$outer:item.name}</td> 00175 <td>{$outer:item.size}</td> 00176 </tr> 00177 {/section} 00178 </table> 00179 00180 {section name=outer loop=$nonexistingvar} 00181 <b><i>Dette skal ikke vises</b></i> 00182 {section-else} 00183 <b><i>This is shown when the {ldelim}$loop{rdelim} variable is non-existant</b></i> 00184 {/section} 00185 00186 00187 Denne koster {1.4|l10n(currency)}<br> 00188 {-123456789|l10n(number)}<br> 00189 {$curdate|l10n(date)}<br> 00190 {$curdate|l10n(shortdate)}<br> 00191 {$curdate|l10n(time)}<br> 00192 {$curdate|l10n(shorttime)}<br> 00193 {include file="test2.tpl"/} 00194 00195 \endcode 00196 */ 00197 00198 class eZTemplate 00199 { 00200 const RESOURCE_FETCH = 1; 00201 const RESOURCE_QUERY = 2; 00202 00203 const ELEMENT_TEXT = 1; 00204 const ELEMENT_SINGLE_TAG = 2; 00205 const ELEMENT_NORMAL_TAG = 3; 00206 const ELEMENT_END_TAG = 4; 00207 const ELEMENT_VARIABLE = 5; 00208 const ELEMENT_COMMENT = 6; 00209 00210 const NODE_ROOT = 1; 00211 const NODE_TEXT = 2; 00212 const NODE_VARIABLE = 3; 00213 const NODE_FUNCTION = 4; 00214 const NODE_OPERATOR = 5; 00215 00216 00217 const NODE_INTERNAL = 100; 00218 const NODE_INTERNAL_CODE_PIECE = 101; 00219 00220 const NODE_INTERNAL_VARIABLE_SET = 105; 00221 const NODE_INTERNAL_VARIABLE_UNSET = 102; 00222 00223 const NODE_INTERNAL_NAMESPACE_CHANGE = 103; 00224 const NODE_INTERNAL_NAMESPACE_RESTORE = 104; 00225 00226 const NODE_INTERNAL_WARNING = 120; 00227 const NODE_INTERNAL_ERROR = 121; 00228 00229 const NODE_INTERNAL_RESOURCE_ACQUISITION = 140; 00230 const NODE_OPTIMIZED_RESOURCE_ACQUISITION = 141; 00231 00232 const NODE_INTERNAL_OUTPUT_ASSIGN = 150; 00233 const NODE_INTERNAL_OUTPUT_READ = 151; 00234 const NODE_INTERNAL_OUTPUT_INCREASE = 152; 00235 const NODE_INTERNAL_OUTPUT_DECREASE = 153; 00236 00237 const NODE_INTERNAL_OUTPUT_SPACING_INCREASE = 160; 00238 const NODE_INTERNAL_SPACING_DECREASE = 161; 00239 00240 const NODE_OPTIMIZED_INIT = 201; 00241 00242 00243 const NODE_USER_CUSTOM = 1000; 00244 00245 00246 const TYPE_VOID = 0; 00247 const TYPE_STRING = 1; 00248 const TYPE_NUMERIC = 2; 00249 const TYPE_IDENTIFIER = 3; 00250 const TYPE_VARIABLE = 4; 00251 const TYPE_ATTRIBUTE = 5; 00252 const TYPE_OPERATOR = 6; 00253 const TYPE_BOOLEAN = 7; 00254 const TYPE_ARRAY = 8; 00255 const TYPE_DYNAMIC_ARRAY = 9; 00256 00257 const TYPE_INTERNAL = 100; 00258 const TYPE_INTERNAL_CODE_PIECE = 101; 00259 const TYPE_PHP_VARIABLE = 102; 00260 00261 const TYPE_OPTIMIZED_NODE = 201; 00262 const TYPE_OPTIMIZED_ARRAY_LOOKUP = 202; 00263 const TYPE_OPTIMIZED_CONTENT_CALL = 203; 00264 const TYPE_OPTIMIZED_ATTRIBUTE_LOOKUP = 204; 00265 00266 const TYPE_INTERNAL_STOP = 999; 00267 00268 00269 const TYPE_STRING_BIT = 1; 00270 const TYPE_NUMERIC_BIT = 2; 00271 const TYPE_IDENTIFIER_BIT = 4; 00272 const TYPE_VARIABLE_BIT = 8; 00273 const TYPE_ATTRIBUTE_BIT = 16; 00274 const TYPE_OPERATOR_BIT = 32; 00275 00276 const TYPE_NONE = 0; 00277 00278 const TYPE_ALL = 63; 00279 00280 const TYPE_BASIC = 47; 00281 00282 const TYPE_MODIFIER_MASK = 48; 00283 00284 const NAMESPACE_SCOPE_GLOBAL = 1; 00285 const NAMESPACE_SCOPE_LOCAL = 2; 00286 const NAMESPACE_SCOPE_RELATIVE = 3; 00287 00288 const DEBUG_INTERNALS = false; 00289 00290 const FILE_ERRORS = 1; 00291 00292 /*! 00293 Intializes the template with left and right delimiters being { and }, 00294 and a file resource. The literal tag "literal" is also registered. 00295 */ 00296 function eZTemplate() 00297 { 00298 $this->Tree = array( eZTemplate::NODE_ROOT, false ); 00299 $this->LDelim = "{"; 00300 $this->RDelim = "}"; 00301 00302 $this->IncludeText = array(); 00303 $this->IncludeOutput = array(); 00304 00305 $this->registerLiteral( "literal" ); 00306 00307 $res = new eZTemplateFileResource(); 00308 $this->DefaultResource = $res; 00309 $this->registerResource( $res ); 00310 00311 $this->Resources = array(); 00312 $this->Text = null; 00313 00314 $this->IsCachingAllowed = true; 00315 00316 $this->resetErrorLog(); 00317 00318 $this->AutoloadPathList = array( 'lib/eztemplate/classes/' ); 00319 $this->Variables = array(); 00320 $this->LocalVariablesNamesStack = array(); 00321 $this->CurrentLocalVariablesNames = null; 00322 $this->Functions = array(); 00323 $this->FunctionAttributes = array(); 00324 00325 $this->TestCompile = false; 00326 00327 $ini = eZINI::instance( 'template.ini' ); 00328 if ( $ini->hasVariable( 'ControlSettings', 'MaxLevel' ) ) 00329 $this->MaxLevel = $ini->variable( 'ControlSettings', 'MaxLevel' ); 00330 00331 $this->MaxLevelWarning = ezpI18n::tr( 'lib/template', 00332 'The maximum nesting level of %max has been reached. The execution is stopped to avoid infinite recursion.', 00333 '', 00334 array( '%max' => $this->MaxLevel ) ); 00335 eZDebug::createAccumulatorGroup( 'template_total', 'Template Total' ); 00336 00337 $this->TemplatesUsageStatistics = array(); 00338 // Array of templates which are used in a single fetch() 00339 $this->TemplateFetchList = array(); 00340 00341 $this->ForeachCounter = 0; 00342 $this->ForCounter = 0; 00343 $this->WhileCounter = 0; 00344 $this->DoCounter = 0; 00345 $this->ElseifCounter = 0; 00346 } 00347 00348 /*! 00349 Returns the left delimiter being used. 00350 */ 00351 function leftDelimiter() 00352 { 00353 return $this->LDelim; 00354 } 00355 00356 /*! 00357 Returns the right delimiter being used. 00358 */ 00359 function rightDelimiter() 00360 { 00361 return $this->RDelim; 00362 } 00363 00364 /*! 00365 Sets the left delimiter. 00366 */ 00367 function setLeftDelimiter( $delim ) 00368 { 00369 $this->LDelim = $delim; 00370 } 00371 00372 /*! 00373 Sets the right delimiter. 00374 */ 00375 function setRightDelimiter( $delim ) 00376 { 00377 $this->RDelim = $delim; 00378 } 00379 00380 /*! 00381 Fetches the result of the template file and displays it. 00382 If $template is supplied it will load this template file first. 00383 */ 00384 function display( $template = false, $extraParameters = false ) 00385 { 00386 $output = $this->fetch( $template, $extraParameters ); 00387 if ( $this->ShowDetails ) 00388 { 00389 echo '<h1>Result:</h1>' . "\n"; 00390 echo '<hr/>' . "\n"; 00391 } 00392 echo "$output"; 00393 if ( $this->ShowDetails ) 00394 { 00395 echo '<hr/>' . "\n"; 00396 } 00397 if ( $this->ShowDetails ) 00398 { 00399 echo "<h1>Template data:</h1>"; 00400 echo "<p class=\"filename\">" . $template . "</p>"; 00401 echo "<pre class=\"example\">" . htmlspecialchars( $this->Text ) . "</pre>"; 00402 reset( $this->IncludeText ); 00403 while ( ( $key = key( $this->IncludeText ) ) !== null ) 00404 { 00405 $item = $this->IncludeText[$key]; 00406 echo "<p class=\"filename\">" . $key . "</p>"; 00407 echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>"; 00408 next( $this->IncludeText ); 00409 } 00410 echo "<h1>Result text:</h1>"; 00411 echo "<p class=\"filename\">" . $template . "</p>"; 00412 echo "<pre class=\"example\">" . htmlspecialchars( $output ) . "</pre>"; 00413 reset( $this->IncludeOutput ); 00414 while ( ( $key = key( $this->IncludeOutput ) ) !== null ) 00415 { 00416 $item = $this->IncludeOutput[$key]; 00417 echo "<p class=\"filename\">" . $key . "</p>"; 00418 echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>"; 00419 next( $this->IncludeOutput ); 00420 } 00421 } 00422 } 00423 00424 /*! 00425 * Initialize list of local variables for the current template. 00426 * The list contains only names of variables. 00427 */ 00428 function createLocalVariablesList() 00429 { 00430 $this->LocalVariablesNamesStack[] = array(); 00431 $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1]; 00432 } 00433 00434 /*! 00435 * Check if the given local variable exists. 00436 */ 00437 function hasLocalVariable( $varName, $rootNamespace ) 00438 { 00439 return ( array_key_exists( $rootNamespace, $this->CurrentLocalVariablesNames ) && 00440 array_key_exists( $varName, $this->CurrentLocalVariablesNames[$rootNamespace] ) ); 00441 } 00442 00443 /*! 00444 * Create a local variable. 00445 */ 00446 function setLocalVariable( $varName, $varValue, $rootNamespace ) 00447 { 00448 $this->CurrentLocalVariablesNames[$rootNamespace][$varName] = 1; 00449 $this->setVariable( $varName, $varValue, $rootNamespace ); 00450 } 00451 00452 /*! 00453 * Destroy a local variable. 00454 */ 00455 function unsetLocalVariable( $varName, $rootNamespace ) 00456 { 00457 if ( !$this->hasLocalVariable( $varName, $rootNamespace ) ) 00458 return; 00459 00460 $this->unsetVariable( $varName, $rootNamespace ); 00461 unset( $this->CurrentLocalVariablesNames[$rootNamespace][$varName] ); 00462 } 00463 00464 /*! 00465 * Destroy all local variables defined in the current template. 00466 */ 00467 function unsetLocalVariables() 00468 { 00469 foreach ( $this->CurrentLocalVariablesNames as $ns => $vars ) 00470 { 00471 foreach ( $vars as $var => $val ) 00472 $this->unsetLocalVariable( $var, $ns ); 00473 } 00474 } 00475 00476 /*! 00477 * Destroy list of local variables defined in the current (innermost) template. 00478 */ 00479 function destroyLocalVariablesList() 00480 { 00481 array_pop( $this->LocalVariablesNamesStack ); 00482 00483 if ( $this->LocalVariablesNamesStack ) 00484 $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1]; 00485 else 00486 unset( $this->CurrentLocalVariablesNames ); 00487 } 00488 00489 /*! 00490 Tries to fetch the result of the template file and returns it. 00491 If $template is supplied it will load this template file first. 00492 */ 00493 function fetch( $template = false, $extraParameters = false, $returnResourceData = false ) 00494 { 00495 $this->resetErrorLog(); 00496 // Reset fetch list when a new fetch is started 00497 $this->TemplateFetchList = array(); 00498 00499 eZDebug::accumulatorStart( 'template_total' ); 00500 eZDebug::accumulatorStart( 'template_load', 'template_total', 'Template load' ); 00501 $root = null; 00502 if ( is_string( $template ) ) 00503 { 00504 $resourceData = $this->loadURIRoot( $template, true, $extraParameters ); 00505 if ( $resourceData and 00506 $resourceData['root-node'] !== null ) 00507 $root =& $resourceData['root-node']; 00508 } 00509 eZDebug::accumulatorStop( 'template_load' ); 00510 if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) ) 00511 { 00512 $savedLocale = setlocale( LC_CTYPE, null ); 00513 setlocale( LC_CTYPE, $resourceData['locales'] ); 00514 } 00515 00516 $text = ""; 00517 00518 if ( $root !== null or 00519 $resourceData['compiled-template'] ) 00520 { 00521 if ( $this->ShowDetails ) 00522 eZDebug::addTimingPoint( "Process" ); 00523 eZDebug::accumulatorStart( 'template_processing', 'template_total', 'Template processing' ); 00524 00525 $templateCompilationUsed = false; 00526 if ( $resourceData['compiled-template'] ) 00527 { 00528 $textElements = array(); 00529 if ( $this->executeCompiledTemplate( $resourceData, $textElements, "", "", $extraParameters ) ) 00530 { 00531 $text = implode( '', $textElements ); 00532 $templateCompilationUsed = true; 00533 } 00534 } 00535 if ( !$templateCompilationUsed ) 00536 { 00537 if ( eZTemplate::isDebugEnabled() ) 00538 { 00539 $fname = $resourceData['template-filename']; 00540 eZDebug::writeDebug( "FETCH START URI: $template, $fname" ); 00541 } 00542 $this->process( $root, $text, "", "" ); 00543 if ( eZTemplate::isDebugEnabled() ) 00544 eZDebug::writeDebug( "FETCH END URI: $template, $fname" ); 00545 } 00546 00547 eZDebug::accumulatorStop( 'template_processing' ); 00548 if ( $this->ShowDetails ) 00549 eZDebug::addTimingPoint( "Process done" ); 00550 } 00551 00552 eZDebug::accumulatorStop( 'template_total' ); 00553 00554 if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) ) 00555 { 00556 setlocale( LC_CTYPE, $savedLocale ); 00557 } 00558 00559 if ( $returnResourceData ) 00560 { 00561 $resourceData['result_text'] = $text; 00562 return $resourceData; 00563 } 00564 return $text; 00565 } 00566 00567 function process( $root, &$text, $rootNamespace, $currentNamespace ) 00568 { 00569 $this->createLocalVariablesList(); 00570 00571 $textElements = array(); 00572 $this->processNode( $root, $textElements, $rootNamespace, $currentNamespace ); 00573 if ( is_array( $textElements ) ) 00574 $text = implode( '', $textElements ); 00575 else 00576 $text = $textElements; 00577 00578 $this->unsetLocalVariables(); 00579 $this->destroyLocalVariablesList(); 00580 } 00581 00582 function processNode( $node, &$textElements, $rootNamespace, $currentNamespace ) 00583 { 00584 $rslt = null; 00585 $nodeType = $node[0]; 00586 if ( $nodeType == eZTemplate::NODE_ROOT ) 00587 { 00588 $children = $node[1]; 00589 if ( $children ) 00590 { 00591 foreach ( $children as $child ) 00592 { 00593 $this->processNode( $child, $textElements, $rootNamespace, $currentNamespace ); 00594 if ( !is_array( $textElements ) ) 00595 eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::root' ); 00596 } 00597 } 00598 } 00599 else if ( $nodeType == eZTemplate::NODE_TEXT ) 00600 { 00601 $textElements[] = $node[2]; 00602 } 00603 else if ( $nodeType == eZTemplate::NODE_VARIABLE ) 00604 { 00605 $variableData = $node[2]; 00606 $variablePlacement = $node[3]; 00607 $this->processVariable( $textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace ); 00608 if ( !is_array( $textElements ) ) 00609 eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::variable' ); 00610 } 00611 else if ( $nodeType == eZTemplate::NODE_FUNCTION ) 00612 { 00613 $functionChildren = $node[1]; 00614 $functionName = $node[2]; 00615 $functionParameters = $node[3]; 00616 $functionPlacement = $node[4]; 00617 $rslt = $this->processFunction( $functionName, $textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace ); 00618 if ( !is_array( $textElements ) ) 00619 eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . "::function( '$functionName' )" ); 00620 } 00621 00622 return $rslt; 00623 } 00624 00625 function processVariable( &$textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace ) 00626 { 00627 $value = $this->elementValue( $variableData, $rootNamespace, $currentNamespace, $variablePlacement ); 00628 $this->appendElementText( $textElements, $value, $rootNamespace, $currentNamespace ); 00629 } 00630 00631 function processFunction( $functionName, &$textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace ) 00632 { 00633 // Note: This code piece is replicated in the eZTemplateCompiler, 00634 // if this code is changed the replicated code must be updated as well. 00635 $func = $this->Functions[$functionName]; 00636 if ( is_array( $func ) ) 00637 { 00638 $this->loadAndRegisterFunctions( $this->Functions[$functionName] ); 00639 $func = $this->Functions[$functionName]; 00640 } 00641 if ( isset( $func ) and 00642 is_object( $func ) ) 00643 { 00644 if ( eZTemplate::isMethodDebugEnabled() ) 00645 eZDebug::writeDebug( "START FUNCTION: $functionName" ); 00646 $value = $func->process( $this, $textElements, $functionName, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace ); 00647 if ( eZTemplate::isMethodDebugEnabled() ) 00648 eZDebug::writeDebug( "END FUNCTION: $functionName" ); 00649 return $value; 00650 } 00651 else 00652 { 00653 $this->warning( "", "Function \"$functionName\" is not registered" ); 00654 return null; 00655 } 00656 } 00657 00658 function fetchFunctionObject( $functionName ) 00659 { 00660 $func = $this->Functions[$functionName]; 00661 if ( is_array( $func ) ) 00662 { 00663 $this->loadAndRegisterFunctions( $this->Functions[$functionName] ); 00664 $func = $this->Functions[$functionName]; 00665 } 00666 return $func; 00667 } 00668 00669 /*! 00670 Loads the template using the URI $uri and parses it. 00671 \return The root node of the tree if \a $returnResourceData is false, 00672 if \c true the entire resource data structure. 00673 */ 00674 function load( $uri, $extraParameters = false, $returnResourceData = false ) 00675 { 00676 $resourceData = $this->loadURIRoot( $uri, true, $extraParameters ); 00677 if ( !$resourceData or 00678 $resourceData['root-node'] === null ) 00679 { 00680 $retValue = null; 00681 return $retValue; 00682 } 00683 else 00684 return $resourceData['root-node']; 00685 } 00686 00687 function parse( $sourceText, &$rootElement, $rootNamespace, &$resourceData ) 00688 { 00689 $parser = eZTemplateMultiPassParser::instance(); 00690 $parser->parse( $this, $sourceText, $rootElement, $rootNamespace, $resourceData ); 00691 } 00692 00693 function loadURIData( $resourceObject, $uri, $resourceName, $template, &$extraParameters, $displayErrors = true ) 00694 { 00695 $resourceData = $this->resourceData( $resourceObject, $uri, $resourceName, $template ); 00696 00697 $resourceData['text'] = null; 00698 $resourceData['root-node'] = null; 00699 $resourceData['compiled-template'] = false; 00700 $resourceData['time-stamp'] = null; 00701 $resourceData['key-data'] = null; 00702 $resourceData['locales'] = null; 00703 00704 if ( !$resourceObject->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters ) ) 00705 { 00706 $resourceData = null; 00707 if ( $displayErrors ) 00708 $this->warning( "", "No template could be loaded for \"$template\" using resource \"$resourceName\"" ); 00709 } 00710 return $resourceData; 00711 } 00712 00713 /*! 00714 \static 00715 Creates a resource data structure of the parameters and returns it. 00716 This structure is passed to various parts of the template system. 00717 00718 \note If you only have the URI you should call resourceFor() first to 00719 figure out the resource handler. 00720 */ 00721 function resourceData( $resourceObject, $uri, $resourceName, $templateName ) 00722 { 00723 $resourceData = array(); 00724 $resourceData['uri'] = $uri; 00725 $resourceData['resource'] = $resourceName; 00726 $resourceData['template-name'] = $templateName; 00727 $resourceData['template-filename'] = $templateName; 00728 $resourceData['handler'] = $resourceObject; 00729 $resourceData['test-compile'] = $this->TestCompile; 00730 return $resourceData; 00731 } 00732 00733 /*! 00734 Loads the template using the URI $uri and returns a structure with the text and timestamp, 00735 false otherwise. 00736 The structure keys are: 00737 - "text", the text. 00738 - "time-stamp", the timestamp. 00739 */ 00740 function loadURIRoot( $uri, $displayErrors = true, &$extraParameters ) 00741 { 00742 $res = ""; 00743 $template = ""; 00744 $resobj = $this->resourceFor( $uri, $res, $template ); 00745 00746 if ( !is_object( $resobj ) ) 00747 { 00748 if ( $displayErrors ) 00749 $this->warning( "", "No resource handler for \"$res\" and no default resource handler, aborting." ); 00750 return null; 00751 } 00752 00753 $canCache = true; 00754 if ( !$resobj->servesStaticData() ) 00755 $canCache = false; 00756 if ( !$this->isCachingAllowed() ) 00757 $canCache = false; 00758 00759 $resourceData = $this->loadURIData( $resobj, $uri, $res, $template, $extraParameters, $displayErrors ); 00760 00761 if ( $resourceData ) 00762 { 00763 eZTemplate::appendTemplateToStatisticsIfNeeded( $resourceData['template-name'], $resourceData['template-filename'] ); 00764 $this->appendTemplateFetch( $resourceData['template-filename'] ); 00765 00766 if ( !$resourceData['compiled-template'] and 00767 $resourceData['root-node'] === null ) 00768 { 00769 $resourceData['root-node'] = array( eZTemplate::NODE_ROOT, false ); 00770 $templateText = $resourceData["text"]; 00771 $keyData = $resourceData['key-data']; 00772 $this->setIncludeText( $uri, $templateText ); 00773 $rootNamespace = ''; 00774 $this->parse( $templateText, $resourceData['root-node'], $rootNamespace, $resourceData ); 00775 00776 if ( eZTemplate::isDebugEnabled() ) 00777 { 00778 $this->appendDebugNodes( $resourceData['root-node'], $resourceData ); 00779 } 00780 00781 if ( $canCache ) 00782 $resobj->setCachedTemplateTree( $keyData, $uri, $res, $template, $extraParameters, $resourceData['root-node'] ); 00783 } 00784 if ( !$resourceData['compiled-template'] and 00785 $canCache and 00786 $this->canCompileTemplate( $resourceData, $extraParameters ) ) 00787 { 00788 $generateStatus = $this->compileTemplate( $resourceData, $extraParameters ); 00789 if ( $generateStatus ) 00790 $resourceData['compiled-template'] = true; 00791 } 00792 } 00793 00794 return $resourceData; 00795 } 00796 00797 function processURI( $uri, $displayErrors = true, &$extraParameters, 00798 &$textElements, $rootNamespace, $currentNamespace ) 00799 { 00800 $this->Level++; 00801 if ( $this->Level > $this->MaxLevel ) 00802 { 00803 eZDebug::writeError( $this->MaxLevelWarning, __METHOD__ . " Level: $this->Level @ $uri" ); 00804 $textElements[] = $this->MaxLevelWarning; 00805 $this->Level--; 00806 return; 00807 } 00808 $resourceData = $this->loadURIRoot( $uri, $displayErrors, $extraParameters ); 00809 if ( !$resourceData or 00810 ( !$resourceData['compiled-template'] and 00811 $resourceData['root-node'] === null ) ) 00812 { 00813 $this->Level--; 00814 return; 00815 } 00816 00817 $templateCompilationUsed = false; 00818 00819 if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) ) 00820 { 00821 $savedLocale = setlocale( LC_CTYPE, null ); 00822 setlocale( LC_CTYPE, $resourceData['locales'] ); 00823 } 00824 00825 if ( $resourceData['compiled-template'] ) 00826 { 00827 if ( $this->executeCompiledTemplate( $resourceData, $textElements, $rootNamespace, $currentNamespace, $extraParameters ) ) 00828 $templateCompilationUsed = true; 00829 } 00830 if ( !$templateCompilationUsed ) 00831 { 00832 $text = null; 00833 if ( eZTemplate::isDebugEnabled() ) 00834 { 00835 $fname = $resourceData['template-filename']; 00836 eZDebug::writeDebug( "START URI: $uri, $fname" ); 00837 } 00838 $this->process( $resourceData['root-node'], $text, $rootNamespace, $currentNamespace ); 00839 if ( eZTemplate::isDebugEnabled() ) 00840 eZDebug::writeDebug( "END URI: $uri, $fname" ); 00841 $this->setIncludeOutput( $uri, $text ); 00842 $textElements[] = $text; 00843 } 00844 00845 if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) ) 00846 { 00847 setlocale( LC_CTYPE, $savedLocale ); 00848 } 00849 $this->Level--; 00850 00851 } 00852 00853 function canCompileTemplate( $resourceData, &$extraParameters ) 00854 { 00855 $resourceObject = $resourceData['handler']; 00856 if ( !$resourceObject ) 00857 return false; 00858 $canGenerate = $resourceObject->canCompileTemplate( $this, $resourceData, $extraParameters ); 00859 return $canGenerate; 00860 } 00861 00862 /*! 00863 Validates the template file \a $file and returns \c true if the file has correct syntax. 00864 \param $returnResourceData If \c true then the returned value will be the resourcedata structure 00865 \sa compileTemplateFile(), fetch() 00866 */ 00867 function validateTemplateFile( $file, $returnResourceData = false ) 00868 { 00869 $this->resetErrorLog(); 00870 00871 if ( !file_exists( $file ) ) 00872 return false; 00873 $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName ); 00874 if ( !$resourceHandler ) 00875 return false; 00876 $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName ); 00877 $resourceData['key-data'] = "file:" . $file; 00878 $extraParameters = array(); 00879 00880 // Disable caching/compiling while fetchin the resource 00881 // It will be restored afterwards 00882 $isCachingAllowed = $this->IsCachingAllowed; 00883 $this->IsCachingAllowed = false; 00884 00885 $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters ); 00886 00887 // Restore previous caching flag 00888 $this->IsCachingAllowed = $isCachingAllowed; 00889 00890 $root =& $resourceData['root-node']; 00891 $root = array( eZTemplate::NODE_ROOT, false ); 00892 $templateText = $resourceData["text"]; 00893 $rootNamespace = ''; 00894 $this->parse( $templateText, $root, $rootNamespace, $resourceData ); 00895 if ( eZTemplate::isDebugEnabled() ) 00896 { 00897 $this->appendDebugNodes( $root, $resourceData ); 00898 } 00899 00900 $result = !$this->hasErrors() and !$this->hasWarnings(); 00901 if ( $returnResourceData ) 00902 { 00903 $resourceData['result'] = $result; 00904 return $resourceData; 00905 } 00906 return $result; 00907 } 00908 00909 /*! 00910 Compiles the template file \a $file and returns \c true if the compilation was OK. 00911 \param $returnResourceData If \c true then the returned value will be the resourcedata structure 00912 \sa validateTemplateFile(), fetch() 00913 */ 00914 function compileTemplateFile( $file, $returnResourceData = false ) 00915 { 00916 $this->resetErrorLog(); 00917 00918 if ( !file_exists( $file ) ) 00919 return false; 00920 $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName ); 00921 if ( !$resourceHandler ) 00922 return false; 00923 $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName ); 00924 $resourceData['key-data'] = "file:" . $file; 00925 $key = md5( $resourceData['key-data'] ); 00926 $extraParameters = array(); 00927 $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters ); 00928 00929 $isCompiled = false; 00930 if ( isset( $resourceData['compiled-template'] ) ) 00931 $isCompiled = $resourceData['compiled-template']; 00932 00933 if ( !$isCompiled ) 00934 { 00935 $root =& $resourceData['root-node']; 00936 $root = array( eZTemplate::NODE_ROOT, false ); 00937 $templateText = $resourceData["text"]; 00938 $rootNamespace = ''; 00939 $this->parse( $templateText, $root, $rootNamespace, $resourceData ); 00940 if ( eZTemplate::isDebugEnabled() ) 00941 { 00942 $this->appendDebugNodes( $root, $resourceData ); 00943 } 00944 00945 $result = eZTemplateCompiler::compileTemplate( $this, $key, $resourceData ); 00946 } 00947 else 00948 { 00949 $result = true; 00950 } 00951 00952 if ( $returnResourceData ) 00953 { 00954 $resourceData['result'] = $result; 00955 return $resourceData; 00956 } 00957 return $result; 00958 } 00959 00960 function compileTemplate( &$resourceData, &$extraParameters ) 00961 { 00962 $resourceObject = $resourceData['handler']; 00963 if ( !$resourceObject ) 00964 return false; 00965 $keyData = $resourceData['key-data']; 00966 $uri = $resourceData['uri']; 00967 $resourceName = $resourceData['resource']; 00968 $templatePath = $resourceData['template-name']; 00969 return $resourceObject->compileTemplate( $this, $keyData, $uri, $resourceName, $templatePath, $extraParameters, $resourceData ); 00970 } 00971 00972 function executeCompiledTemplate( &$resourceData, &$textElements, $rootNamespace, $currentNamespace, &$extraParameters ) 00973 { 00974 $resourceObject = $resourceData['handler']; 00975 if ( !$resourceObject ) 00976 return false; 00977 $keyData = $resourceData['key-data']; 00978 $uri = $resourceData['uri']; 00979 $resourceName = $resourceData['resource']; 00980 $templatePath = $resourceData['template-name']; 00981 $timestamp = $resourceData['time-stamp']; 00982 return $resourceObject->executeCompiledTemplate( $this, $textElements, 00983 $keyData, $uri, $resourceData, $templatePath, 00984 $extraParameters, $timestamp, 00985 $rootNamespace, $currentNamespace ); 00986 } 00987 00988 /*! 00989 Returns the resource object for URI $uri. If a resource type is specified 00990 in the URI it is extracted and set in $res. The template name is set in $template 00991 without any resource specifier. To specify a resource the name and a ":" is 00992 prepended to the URI, for instance file:my.tpl. 00993 If no resource type is found the URI the default resource handler is used. 00994 */ 00995 function resourceFor( $uri, &$res, &$template ) 00996 { 00997 $args = explode( ":", $uri ); 00998 if ( isset( $args[1] ) ) 00999 { 01000 $res = $args[0]; 01001 $template = $args[1]; 01002 } 01003 else 01004 $template = $uri; 01005 if ( eZTemplate::isDebugEnabled() ) 01006 { 01007 eZDebug::writeNotice( "eZTemplate: Loading template \"$template\" with resource \"$res\"" ); 01008 } 01009 if ( isset( $this->Resources[$res] ) and is_object( $this->Resources[$res] ) ) 01010 { 01011 return $this->Resources[$res]; 01012 } 01013 return $this->DefaultResource; 01014 } 01015 01016 /*! 01017 \return The resource handler object for resource name \a $resourceName. 01018 \sa resourceFor 01019 */ 01020 function resourceHandler( $resourceName ) 01021 { 01022 if ( isset( $this->Resources[$resourceName] ) && 01023 is_object( $this->Resources[$resourceName] ) ) 01024 { 01025 return $this->Resources[$resourceName]; 01026 } 01027 return $this->DefaultResource; 01028 } 01029 01030 function hasChildren( &$function, $functionName ) 01031 { 01032 $hasChildren = $function->hasChildren(); 01033 if ( is_array( $hasChildren ) ) 01034 return $hasChildren[$functionName]; 01035 else 01036 return $hasChildren; 01037 } 01038 01039 /*! 01040 Returns the empty variable type. 01041 */ 01042 function emptyVariable() 01043 { 01044 return array( "type" => "null" ); 01045 } 01046 01047 /*! 01048 \static 01049 */ 01050 function mergeNamespace( $rootNamespace, $additionalNamespace ) 01051 { 01052 $namespace = $rootNamespace; 01053 if ( $namespace == '' ) 01054 $namespace = $additionalNamespace; 01055 else if ( $additionalNamespace != '' ) 01056 $namespace = "$namespace:$additionalNamespace"; 01057 return $namespace; 01058 } 01059 01060 /*! 01061 Returns the actual value of a template type or null if an unknown type. 01062 */ 01063 function elementValue( &$dataElements, $rootNamespace, $currentNamespace, $placement = false, 01064 $checkExistance = false, $checkForProxy = false ) 01065 { 01066 /* 01067 * We use a small dirty hack in this function... 01068 * To help the caller to determine if the value was a proxy object, 01069 * we store boolean true to $dataElements['proxy-object-found'] in this case. 01070 * (it's up to caller to remove this garbage from $dataElements...) 01071 * This behaviour is enabled by $checkForProxy parameter. 01072 */ 01073 01074 $value = null; 01075 if ( !is_array( $dataElements ) ) 01076 { 01077 $this->error( "elementValue", 01078 "Missing array data structure, got " . gettype( $dataElements ) ); 01079 return null; 01080 } 01081 foreach ( $dataElements as $dataElement ) 01082 { 01083 if ( $dataElement === null ) 01084 { 01085 return null; 01086 } 01087 $dataType = $dataElement[0]; 01088 switch ( $dataType ) 01089 { 01090 case eZTemplate::TYPE_VOID: 01091 { 01092 if ( !$checkExistance ) 01093 $this->warning( 'elementValue', 01094 'Found void datatype, should not be used' ); 01095 else 01096 { 01097 return null; 01098 } 01099 } break; 01100 case eZTemplate::TYPE_STRING: 01101 case eZTemplate::TYPE_NUMERIC: 01102 case eZTemplate::TYPE_IDENTIFIER: 01103 case eZTemplate::TYPE_BOOLEAN: 01104 case eZTemplate::TYPE_ARRAY: 01105 { 01106 $value = $dataElement[1]; 01107 } break; 01108 case eZTemplate::TYPE_VARIABLE: 01109 { 01110 $variableData = $dataElement[1]; 01111 $variableNamespace = $variableData[0]; 01112 $variableNamespaceScope = $variableData[1]; 01113 $variableName = $variableData[2]; 01114 if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_GLOBAL ) 01115 $namespace = $variableNamespace; 01116 else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_LOCAL ) 01117 $namespace = $this->mergeNamespace( $rootNamespace, $variableNamespace ); 01118 else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_RELATIVE ) 01119 $namespace = $this->mergeNamespace( $currentNamespace, $variableNamespace ); 01120 else 01121 $namespace = false; 01122 if ( $this->hasVariable( $variableName, $namespace ) ) 01123 { 01124 $value = $this->variable( $variableName, $namespace ); 01125 } 01126 else 01127 { 01128 if ( !$checkExistance ) 01129 $this->error( '', "Unknown template variable '$variableName' in namespace '$namespace'", $placement ); 01130 { 01131 return null; 01132 } 01133 } 01134 } break; 01135 case eZTemplate::TYPE_ATTRIBUTE: 01136 { 01137 $attributeData = $dataElement[1]; 01138 $attributeValue = $this->elementValue( $attributeData, $rootNamespace, $currentNamespace, false, $checkExistance ); 01139 01140 if ( $attributeValue !== null ) 01141 { 01142 if ( !is_numeric( $attributeValue ) and 01143 !is_string( $attributeValue ) and 01144 !is_bool( $attributeValue ) ) 01145 { 01146 if ( !$checkExistance ) 01147 $this->error( "", 01148 "Cannot use type " . gettype( $attributeValue ) . " for attribute lookup", $placement ); 01149 { 01150 return null; 01151 } 01152 } 01153 if ( is_array( $value ) ) 01154 { 01155 if ( array_key_exists( $attributeValue, $value ) ) 01156 { 01157 $value = $value[$attributeValue]; 01158 } 01159 else 01160 { 01161 if ( !$checkExistance ) 01162 { 01163 $arrayAttributeList = array_keys( $value ); 01164 $arrayCount = count( $arrayAttributeList ); 01165 $errorMessage = "No such attribute for array($arrayCount): $attributeValue"; 01166 $chooseText = "Choose one of following: "; 01167 $errorMessage .= "\n$chooseText"; 01168 $errorMessage .= $this->expandAttributes( $arrayAttributeList, $chooseText, 25 ); 01169 $this->error( "", 01170 $errorMessage, $placement ); 01171 } 01172 return null; 01173 } 01174 } 01175 else if ( is_object( $value ) ) 01176 { 01177 if ( method_exists( $value, "attribute" ) and 01178 method_exists( $value, "hasAttribute" ) ) 01179 { 01180 if ( $value->hasAttribute( $attributeValue ) ) 01181 { 01182 $value = $value->attribute( $attributeValue ); 01183 } 01184 else 01185 { 01186 if ( !$checkExistance ) 01187 { 01188 $objectAttributeList = array(); 01189 if ( method_exists( $value, 'attributes' ) ) 01190 $objectAttributeList = $value->attributes(); 01191 $objectClass= get_class( $value ); 01192 $errorMessage = "No such attribute for object($objectClass): $attributeValue"; 01193 $chooseText = "Choose one of following: "; 01194 $errorMessage .= "\n$chooseText"; 01195 $errorMessage .= $this->expandAttributes( $objectAttributeList, $chooseText, 25 ); 01196 $this->error( "", 01197 $errorMessage, $placement ); 01198 } 01199 return null; 01200 } 01201 } 01202 else 01203 { 01204 if ( !$checkExistance ) 01205 $this->error( "", 01206 "Cannot retrieve attribute of object(" . get_class( $value ) . 01207 "), no attribute functions available", 01208 $placement ); 01209 return null; 01210 } 01211 } 01212 else 01213 { 01214 if ( !$checkExistance ) 01215 $this->error( "", 01216 "Cannot retrieve attribute of a " . gettype( $value ), 01217 $placement ); 01218 return null; 01219 } 01220 } 01221 else 01222 { 01223 if ( !$checkExistance ) 01224 $this->error( '', 01225 'Attribute value was null, cannot get attribute', 01226 $placement ); 01227 return null; 01228 } 01229 } break; 01230 case eZTemplate::TYPE_OPERATOR: 01231 { 01232 $operatorParameters = $dataElement[1]; 01233 $operatorName = $operatorParameters[0]; 01234 $operatorParameters = array_splice( $operatorParameters, 1 ); 01235 if ( is_object( $value ) and 01236 method_exists( $value, 'templateValue' ) ) 01237 { 01238 if ( $checkForProxy ) 01239 $dataElements['proxy-object-found'] = true; 01240 $value = $value->templateValue(); 01241 } 01242 $valueData = array( 'value' => $value ); 01243 $this->processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace, 01244 $valueData, $placement, $checkExistance ); 01245 $value = $valueData['value']; 01246 } break; 01247 default: 01248 { 01249 if ( !$checkExistance ) 01250 $this->error( "elementValue", 01251 "Unknown data type: '$dataType'" ); 01252 return null; 01253 } 01254 } 01255 } 01256 if ( is_object( $value ) and 01257 method_exists( $value, 'templateValue' ) ) 01258 { 01259 if ( $checkForProxy ) 01260 $dataElements['proxy-object-found'] = true; 01261 return $value->templateValue(); 01262 } 01263 return $value; 01264 } 01265 01266 function expandAttributes( $attributeList, $chooseText, $maxThreshold, $minThreshold = 1 ) 01267 { 01268 $errorMessage = ''; 01269 $attributeCount = count( $attributeList ); 01270 if ( $attributeCount < $minThreshold ) 01271 return $errorMessage; 01272 if ( $attributeCount < $maxThreshold ) 01273 { 01274 $chooseLength = strlen( $chooseText ); 01275 $attributeText = ''; 01276 $i = 0; 01277 foreach ( $attributeList as $attributeName ) 01278 { 01279 if ( $i > 0 ) 01280 $attributeText .= ","; 01281 if ( strlen( $attributeText ) > 40 ) 01282 { 01283 $attributeText .= "\n"; 01284 $errorMessage .= $attributeText; 01285 $errorMessage .= str_repeat( ' ', $chooseLength ); 01286 $attributeText = ''; 01287 } 01288 else if ( $i > 0 ) 01289 $attributeText .= " "; 01290 $attributeText .= $attributeName; 01291 ++$i; 01292 } 01293 $errorMessage .= $attributeText; 01294 } 01295 return $errorMessage; 01296 } 01297 01298 function processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace, 01299 &$valueData, $placement = false, $checkExistance = false ) 01300 { 01301 $namedParameters = array(); 01302 $operatorParameterDefinition = $this->operatorParameterList( $operatorName ); 01303 $i = 0; 01304 foreach ( $operatorParameterDefinition as $parameterName => $parameterType ) 01305 { 01306 if ( !isset( $operatorParameters[$i] ) or 01307 !isset( $operatorParameters[$i][0] ) or 01308 $operatorParameters[$i][0] == eZTemplate::TYPE_VOID ) 01309 { 01310 if ( $parameterType["required"] ) 01311 { 01312 if ( !$checkExistance ) 01313 $this->warning( "eZTemplateOperatorElement", "Parameter '$parameterName' ($i) missing", 01314 $placement ); 01315 $namedParameters[$parameterName] = $parameterType["default"]; 01316 } 01317 else 01318 { 01319 $namedParameters[$parameterName] = $parameterType["default"]; 01320 } 01321 } 01322 else 01323 { 01324 $parameterData = $operatorParameters[$i]; 01325 $namedParameters[$parameterName] = $this->elementValue( $parameterData, $rootNamespace, $currentNamespace, false, $checkExistance ); 01326 } 01327 ++$i; 01328 } 01329 01330 if ( isset( $this->Operators[$operatorName] ) ) 01331 { 01332 if ( is_array( $this->Operators[$operatorName] ) ) 01333 { 01334 $this->loadAndRegisterOperators( $this->Operators[$operatorName] ); 01335 } 01336 01337 $op = $this->Operators[$operatorName]; 01338 01339 if ( is_object( $op ) and method_exists( $op, 'modify' ) ) 01340 { 01341 $value = $valueData['value']; 01342 if ( eZTemplate::isMethodDebugEnabled() ) 01343 eZDebug::writeDebug( "START OPERATOR: $operatorName" ); 01344 $op->modify( $this, $operatorName, $operatorParameters, $rootNamespace, $currentNamespace, $value, $namedParameters, 01345 $placement ); 01346 if ( eZTemplate::isMethodDebugEnabled() ) 01347 eZDebug::writeDebug( "END OPERATOR: $operatorName" ); 01348 $valueData['value'] = $value; 01349 } 01350 else 01351 $this->error( '', "Object problem with operator '$operatorName' ", 01352 $placement ); 01353 } 01354 else if ( !$checkExistance ) 01355 $this->warning( "", "Operator '$operatorName' is not registered", 01356 $placement ); 01357 } 01358 01359 /*! 01360 Return the identifier used for attribute lookup. 01361 */ 01362 function attributeValue( &$data, $nspace ) 01363 { 01364 switch ( $data["type"] ) 01365 { 01366 case "map": 01367 { 01368 return $data["content"]; 01369 } break; 01370 case "index": 01371 { 01372 return $data["content"]; 01373 } break; 01374 case "variable": 01375 { 01376 return $this->elementValue( $data["content"], $nspace ); 01377 } break; 01378 default: 01379 { 01380 $this->error( "attributeValue()", "Unknown attribute type: " . $data["type"] ); 01381 return null; 01382 } 01383 } 01384 } 01385 01386 /*! 01387 Helper function for creating a displayable text for a variable. 01388 */ 01389 function variableText( $var, $namespace = "", $attrs = array() ) 01390 { 01391 $txt = "$"; 01392 if ( $namespace != "" ) 01393 $txt .= "$namespace:"; 01394 $txt .= $var; 01395 if ( !empty( $attrs ) ) 01396 $txt .= "." . implode( ".", $attrs ); 01397 return $txt; 01398 } 01399 01400 /*! 01401 Returns the named parameter list for the operator $name. 01402 */ 01403 function operatorParameterList( $name ) 01404 { 01405 $param_list = array(); 01406 if ( !isset( $this->Operators[$name] ) ) 01407 { 01408 return $param_list; 01409 } 01410 01411 if ( is_array( $this->Operators[$name] ) ) 01412 { 01413 $this->loadAndRegisterOperators( $this->Operators[$name] ); 01414 } 01415 01416 $op = $this->Operators[$name]; 01417 if ( isset( $op ) and 01418 method_exists( $op, "namedParameterList" ) ) 01419 { 01420 $param_list = $op->namedParameterList(); 01421 if ( method_exists( $op, "namedParameterPerOperator" ) and 01422 $op->namedParameterPerOperator() ) 01423 { 01424 if ( !isset( $param_list[$name] ) ) 01425 return array(); 01426 $param_list = $param_list[$name]; 01427 } 01428 } 01429 return $param_list; 01430 } 01431 01432 /*! 01433 Tries to run the operator $operatorName with parameters $operatorParameters 01434 on the value $value. 01435 */ 01436 function doOperator( $element, &$namespace, &$current_nspace, &$value, $operatorName, $operatorParameters, &$named_params ) 01437 { 01438 if ( is_array( $this->Operators[$operatorName] ) ) 01439 { 01440 $this->loadAndRegisterOperators( $this->Operators[$operatorName] ); 01441 } 01442 $op = $this->Operators[$operatorName]; 01443 if ( isset( $op ) ) 01444 { 01445 $op->modify( $element, $this, $operatorName, $operatorParameters, $namespace, $current_nspace, $value, $named_params ); 01446 } 01447 else 01448 $this->warning( "", "Operator \"$operatorName\" is not registered" ); 01449 } 01450 01451 /*! 01452 Tries to run the function object $func_obj 01453 */ 01454 function doFunction( $name, $func_obj, $nspace, $current_nspace ) 01455 { 01456 $func = $this->Functions[$name]; 01457 if ( is_array( $func ) ) 01458 { 01459 $this->loadAndRegisterFunctions( $this->Functions[$name] ); 01460 $func = $this->Functions[$name]; 01461 } 01462 if ( isset( $func ) and 01463 is_object( $func ) ) 01464 { 01465 return $func->process( $this, $name, $func_obj, $nspace, $current_nspace ); 01466 } 01467 else 01468 { 01469 $this->warning( "", "Function \"$name\" is not registered" ); 01470 return false; 01471 } 01472 } 01473 01474 /** 01475 * Sets the template variable $var to the value $val. 01476 * 01477 * @param string $var 01478 * @param string $val 01479 * @param string $namespace (optional) 01480 * @param bool $scopeSafe If true, will assure that $var is not overridden for $namespace. False by default 01481 */ 01482 function setVariable( $var, $val, $namespace = '', $scopeSafe = false ) 01483 { 01484 if ( $scopeSafe && isset( $this->Variables[$namespace][$var] ) ) 01485 { 01486 $safeNamespace = $namespace; 01487 do 01488 { 01489 $safeNamespace .= ':safe'; 01490 } 01491 while( isset( $this->Variables[$safeNamespace][$var] ) ); 01492 01493 $this->Variables[$safeNamespace][$var] = $this->Variables[$namespace][$var]; 01494 } 01495 01496 $this->Variables[$namespace][$var] = $val; 01497 } 01498 01499 /** 01500 * Sets the template variable $var to the value $val by ref 01501 * 01502 * @deprecated Since 4.4, have not used references since 3.10 01503 * @uses eZTemplate::setVariable() 01504 * 01505 * @param string $var 01506 * @param string $val 01507 * @param string $namespace (optional) 01508 */ 01509 function setVariableRef( $var, $val, $namespace = '' ) 01510 { 01511 $this->setVariable( $var, $val, $namespace ); 01512 } 01513 01514 /** 01515 * Unsets the template variable $var. 01516 * 01517 * @param string $var 01518 * @param string $namespace (optional) 01519 */ 01520 function unsetVariable( $var, $namespace = '' ) 01521 { 01522 if ( isset( $this->Variables[$namespace] ) && 01523 array_key_exists( $var, $this->Variables[$namespace] ) ) 01524 { 01525 $safeNamespace = "{$namespace}:safe"; 01526 if ( isset( $this->Variables[$safeNamespace][$var] ) ) 01527 { 01528 // Check if a nested safe namespace for $var 01529 // If true, then add a level of testing and test again 01530 while( isset( $this->Variables["{$safeNamespace}:safe"][$var] ) ) 01531 { 01532 $safeNamespace .= ':safe'; 01533 } 01534 01535 // Get the $var backup back and delete it 01536 $this->Variables[$namespace][$var] = $this->Variables[$safeNamespace][$var]; 01537 unset( $this->Variables[$safeNamespace][$var] ); 01538 } 01539 else 01540 { 01541 unset( $this->Variables[$namespace][$var] ); 01542 } 01543 } 01544 else 01545 { 01546 $this->warning( "unsetVariable()", "Undefined Variable: \${$namespace}:{$var}, cannot unset" ); 01547 } 01548 } 01549 01550 /** 01551 * Returns true if the variable $var is set in namespace $namespace, 01552 * if $attrs is supplied all attributes must exist for the function to return true. 01553 * 01554 * @param string $var 01555 * @param string $namespace (optional) 01556 * @param array $attrs (optional) Deprecated as of 4.4. 01557 * @return bool 01558 */ 01559 function hasVariable( $var, $namespace = '', $attrs = null ) 01560 { 01561 $exists = ( isset( $this->Variables[$namespace] ) && 01562 array_key_exists( $var, $this->Variables[$namespace] ) ); 01563 if ( $exists && $attrs !== null && !empty( $attrs ) ) 01564 { 01565 eZDebug::writeStrict( '$attrs parameter is deprecated as of 4.4', __METHOD__ ); 01566 $ptr =& $this->Variables[$namespace][$var]; 01567 foreach( $attrs as $attr ) 01568 { 01569 unset( $tmp ); 01570 if ( is_object( $ptr ) ) 01571 { 01572 if ( $ptr->hasAttribute( $attr ) ) 01573 $tmp = $ptr->attribute( $attr ); 01574 else 01575 return false; 01576 } 01577 else if ( is_array( $ptr ) ) 01578 { 01579 if ( array_key_exists( $attr, $ptr ) ) 01580 $tmp =& $ptr[$attr]; 01581 else 01582 return false; 01583 } 01584 else 01585 { 01586 return false; 01587 } 01588 unset( $ptr ); 01589 $ptr =& $tmp; 01590 } 01591 } 01592 return $exists; 01593 } 01594 01595 /** 01596 * Returns the content of the variable $var using namespace $namespace, 01597 * if $attrs is supplied the result of the attributes is returned. 01598 * 01599 * @param string $var 01600 * @param string $namespace (optional) 01601 * @param array $attrs (optional) Deprecated as of 4.4 01602 * @return string|array 01603 */ 01604 function variable( $var, $namespace = '', $attrs = null ) 01605 { 01606 $val = null; 01607 $exists = ( isset( $this->Variables[$namespace] ) && 01608 array_key_exists( $var, $this->Variables[$namespace] ) ); 01609 if ( $exists ) 01610 { 01611 if ( $attrs !== null && !empty( $attrs ) ) 01612 { 01613 eZDebug::writeStrict( '$attrs parameter is deprecated as of 4.4', __METHOD__ ); 01614 $element = $this->Variables[$namespace][$var]; 01615 foreach( $attrs as $attr ) 01616 { 01617 if ( is_object( $element ) ) 01618 { 01619 if ( $element->hasAttribute( $attr ) ) 01620 { 01621 $element = $element->attribute( $attr ); 01622 } 01623 else 01624 { 01625 return $val; 01626 } 01627 } 01628 else if ( is_array( $element ) ) 01629 { 01630 if ( array_key_exists( $attr, $element ) ) 01631 { 01632 $val = $element[$attr]; 01633 } 01634 else 01635 { 01636 return $val; 01637 } 01638 } 01639 else 01640 { 01641 return $val; 01642 } 01643 $val = $element; 01644 } 01645 } 01646 else 01647 { 01648 $val = $this->Variables[$namespace][$var]; 01649 } 01650 } 01651 return $val; 01652 } 01653 01654 /*! 01655 Returns the attribute(s) of the template variable $var, 01656 $attrs is an array of attribute names to use iteratively for each new variable returned. 01657 */ 01658 function variableAttribute( $var, $attrs ) 01659 { 01660 foreach( $attrs as $attr ) 01661 { 01662 if ( is_object( $var ) ) 01663 { 01664 if ( $var->hasAttribute( $attr ) ) 01665 { 01666 $var = $var->attribute( $attr ); 01667 } 01668 else 01669 { 01670 return null; 01671 } 01672 } 01673 else if ( is_array( $var ) ) 01674 { 01675 if ( isset( $var[$attr] ) ) 01676 { 01677 $var = $var[$attr]; 01678 } 01679 else 01680 { 01681 return null; 01682 } 01683 } 01684 else 01685 { 01686 return null; 01687 } 01688 } 01689 if ( isset( $var ) ) 01690 { 01691 return $var; 01692 } 01693 01694 return null; 01695 } 01696 01697 function appendElement( &$text, $item, $nspace, $name ) 01698 { 01699 $this->appendElementText( $textElements, $item, $nspace, $name ); 01700 $text .= implode( '', $textElements ); 01701 } 01702 01703 function appendElementText( &$textElements, $item, $nspace, $name ) 01704 { 01705 if ( !is_array( $textElements ) ) 01706 $textElements = array(); 01707 if ( is_object( $item ) and 01708 method_exists( $item, 'templateValue' ) ) 01709 { 01710 $item = $item->templateValue(); 01711 $textElements[] = "$item"; 01712 } 01713 else if ( is_object( $item ) ) 01714 { 01715 $hasTemplateData = false; 01716 if ( method_exists( $item, 'templateData' ) ) 01717 { 01718 $templateData = $item->templateData(); 01719 if ( is_array( $templateData ) and 01720 isset( $templateData['type'] ) ) 01721 { 01722 if ( $templateData['type'] == 'template' and 01723 isset( $templateData['uri'] ) and 01724 isset( $templateData['template_variable_name'] ) ) 01725 { 01726 $templateURI =& $templateData['uri']; 01727 $templateVariableName =& $templateData['template_variable_name']; 01728 $templateText = ''; 01729 $this->setVariable( $templateVariableName, $item, $name ); 01730 eZTemplateIncludeFunction::handleInclude( $textElements, $templateURI, $this, $nspace, $name ); 01731 $hasTemplateData = true; 01732 } 01733 } 01734 } 01735 if ( !$hasTemplateData ) 01736 $textElements[] = method_exists( $item, '__toString' ) ? (string)$item : 'Object(' . get_class( $item ) . ')'; 01737 } 01738 else 01739 $textElements[] = "$item"; 01740 return $textElements; 01741 } 01742 01743 /*! 01744 Creates some text nodes before and after the children of \a $root. 01745 It will extract the current filename and uri and create some XHTML 01746 comments and inline text. 01747 \sa isXHTMLCodeIncluded 01748 */ 01749 function appendDebugNodes( &$root, &$resourceData ) 01750 { 01751 $path = $resourceData['template-filename']; 01752 // Do not ouput debug on pagelayout templates to avoid trigering 01753 // browser quirks mode 01754 if ( isset( $root[1][0][2] ) && is_string( $root[1][0][2] ) && stripos( $root[1][0][2], '<!DOCTYPE' ) === 0 ) 01755 return; 01756 $uri = $resourceData['uri']; 01757 $preText = "\n<!-- START: including template: $path ($uri) -->\n"; 01758 if ( eZTemplate::isXHTMLCodeIncluded() ) 01759 $preText .= "<p class=\"small\">$path</p><br/>\n"; 01760 $postText = "\n<!-- STOP: including template: $path ($uri) -->\n"; 01761 01762 $preNode = eZTemplateNodeTool::createTextNode( $preText ); 01763 $postNode = eZTemplateNodeTool::createTextNode( $postText ); 01764 01765 if ( is_array( $root[1] ) ) 01766 { 01767 $root[1] = array_merge( array( $preNode ), $root[1] ); 01768 } 01769 else 01770 { 01771 $root[1] = array( $preNode ); 01772 } 01773 $root[1][] = $postNode; 01774 } 01775 01776 /*! 01777 Registers the functions supplied by the object $functionObject. 01778 The object must have a function called functionList() 01779 which returns an array of functions this object handles. 01780 If the object has a function called attributeList() 01781 it is used for registering function attributes. 01782 The function returns an associative array with each key being 01783 the name of the function and the value being a boolean. 01784 If the boolean is true the function will have children. 01785 */ 01786 function registerFunctions( &$functionObject ) 01787 { 01788 $this->registerFunctionsInternal( $functionObject ); 01789 } 01790 01791 function registerAutoloadFunctions( $functionDefinition ) 01792 { 01793 if ( ( ( isset( $functionDefinition['function'] ) || 01794 isset( $functionDefinition['class'] ) ) && 01795 ( isset( $functionDefinition['function_names_function'] ) || 01796 isset( $functionDefinition['function_names'] ) ) ) ) 01797 { 01798 if ( isset( $functionDefinition['function_names_function'] ) ) 01799 { 01800 $functionNamesFunction = $functionDefinition['function_names_function']; 01801 if ( !function_exists( $functionNamesFunction ) ) 01802 { 01803 $this->error( 'registerFunctions', "Cannot register function definition, missing function names function '$functionNamesFunction'" ); 01804 return; 01805 } 01806 $functionNames = $functionNamesFunction(); 01807 } 01808 else 01809 $functionNames = $functionDefinition['function_names']; 01810 foreach ( $functionNames as $functionName ) 01811 { 01812 $this->Functions[$functionName] = $functionDefinition; 01813 } 01814 if ( isset( $functionDefinition['function_attributes'] ) ) 01815 { 01816 foreach ( $functionDefinition['function_attributes'] as $functionAttributeName ) 01817 { 01818 $this->FunctionAttributes[$functionAttributeName] = $functionDefinition; 01819 } 01820 } 01821 } 01822 else 01823 $this->error( 'registerFunctions', 'Cannot register function definition, missing data' ); 01824 } 01825 01826 function loadAndRegisterFunctions( $functionDefinition ) 01827 { 01828 eZDebug::accumulatorStart( 'template_register_function', 'template_total', 'Template load and register function' ); 01829 $functionObject = null; 01830 if ( isset( $functionDefinition['function'] ) ) 01831 { 01832 $function = $functionDefinition['function']; 01833 if ( function_exists( $function ) ) 01834 $functionObject = $function(); 01835 } 01836 else 01837 { 01838 if ( !class_exists( $functionDefinition['class'], false ) 01839 && isset( $functionDefinition['script'] ) ) 01840 { 01841 include_once( $functionDefinition['script'] ); 01842 } 01843 $class = $functionDefinition['class']; 01844 if ( class_exists( $class ) ) 01845 $functionObject = new $class(); 01846 } 01847 eZDebug::accumulatorStop( 'template_register_function' ); 01848 if ( is_object( $functionObject ) ) 01849 { 01850 $this->registerFunctionsInternal( $functionObject, true ); 01851 return true; 01852 } 01853 return false; 01854 } 01855 01856 /*! 01857 \private 01858 */ 01859 function registerFunctionsInternal( $functionObject, $debug = false ) 01860 { 01861 if ( !is_object( $functionObject ) or 01862 !method_exists( $functionObject, 'functionList' ) ) 01863 return false; 01864 foreach ( $functionObject->functionList() as $functionName ) 01865 { 01866 $this->Functions[$functionName] = $functionObject; 01867 } 01868 if ( method_exists( $functionObject, "attributeList" ) ) 01869 { 01870 $functionAttributes = $functionObject->attributeList(); 01871 foreach ( $functionAttributes as $attributeName => $hasChildren ) 01872 { 01873 $this->FunctionAttributes[$attributeName] = $hasChildren; 01874 } 01875 } 01876 return true; 01877 } 01878 01879 /*! 01880 Registers the function $func_name to be bound to object $func_obj. 01881 If the object has a function called attributeList() 01882 it is used for registering function attributes. 01883 The function returns an associative array with each key being 01884 the name of the function and the value being a boolean. 01885 If the boolean is true the function will have children. 01886 */ 01887 function registerFunction( $func_name, $func_obj ) 01888 { 01889 $this->Functions[$func_name] = $func_obj; 01890 if ( method_exists( $func_obj, "attributeList" ) ) 01891 { 01892 $attrs = $func_obj->attributeList(); 01893 while ( list( $attr_name, $has_children ) = each( $attrs ) ) 01894 { 01895 $this->FunctionAttributes[$attr_name] = $has_children; 01896 } 01897 } 01898 } 01899 01900 /*! 01901 Registers a new literal tag in which the tag will be transformed into 01902 a text element. 01903 */ 01904 function registerLiteral( $func_name ) 01905 { 01906 $this->Literals[$func_name] = true; 01907 } 01908 01909 /*! 01910 Removes the literal tag $func_name. 01911 */ 01912 function unregisterLiteral( $func_name ) 01913 { 01914 unset( $this->Literals[$func_name] ); 01915 } 01916 01917 function registerAutoloadOperators( $operatorDefinition ) 01918 { 01919 if ( ( ( isset( $operatorDefinition['function'] ) || 01920 isset( $operatorDefinition['class'] ) ) && 01921 ( isset( $operatorDefinition['operator_names_function'] ) || 01922 isset( $operatorDefinition['operator_names'] ) ) ) ) 01923 { 01924 if ( isset( $operatorDefinition['operator_names_function'] ) ) 01925 { 01926 $operatorNamesFunction = $operatorDefinition['operator_names_function']; 01927 if ( !function_exists( $operatorNamesFunction ) ) 01928 { 01929 $this->error( 'registerOperators', "Cannot register operator definition, missing operator names function '$operatorNamesFunction'" ); 01930 return; 01931 } 01932 $operatorNames = $operatorNamesFunction(); 01933 } 01934 else 01935 $operatorNames = $operatorDefinition['operator_names']; 01936 foreach ( $operatorNames as $operatorName ) 01937 { 01938 $this->Operators[$operatorName] = $operatorDefinition; 01939 } 01940 } 01941 else 01942 $this->error( 'registerOperators', 'Cannot register operator definition, missing data' ); 01943 } 01944 01945 function loadAndRegisterOperators( $operatorDefinition ) 01946 { 01947 $operatorObject = null; 01948 if ( isset( $operatorDefinition['function'] ) ) 01949 { 01950 $function = $operatorDefinition['function']; 01951 if ( function_exists( $function ) ) 01952 $operatorObject = $function(); 01953 } 01954 else 01955 { 01956 $class = $operatorDefinition['class']; 01957 if ( !class_exists( $class, false ) && isset( $operatorDefinition['script'] ) ) 01958 { 01959 include_once( $operatorDefinition['script'] ); 01960 } 01961 if ( class_exists( $class ) ) 01962 { 01963 if ( isset( $operatorDefinition['class_parameter'] ) ) 01964 $operatorObject = new $class( $operatorDefinition['class_parameter'] ); 01965 else 01966 $operatorObject = new $class(); 01967 } 01968 } 01969 if ( is_object( $operatorObject ) ) 01970 { 01971 $this->registerOperatorsInternal( $operatorObject, true ); 01972 return true; 01973 } 01974 return false; 01975 } 01976 01977 /*! 01978 Registers the operators supplied by the object $operatorObject. 01979 The function operatorList() must return an array of operator names. 01980 */ 01981 function registerOperators( &$operatorObject ) 01982 { 01983 $this->registerOperatorsInternal( $operatorObject ); 01984 } 01985 01986 function registerOperatorsInternal( $operatorObject, $debug = false ) 01987 { 01988 if ( !is_object( $operatorObject ) or 01989 !method_exists( $operatorObject, 'operatorList' ) ) 01990 return false; 01991 foreach( $operatorObject->operatorList() as $operatorName ) 01992 { 01993 $this->Operators[$operatorName] = $operatorObject; 01994 } 01995 } 01996 01997 /*! 01998 Registers the operator $op_name to use the object $op_obj. 01999 */ 02000 function registerOperator( $op_name, $op_obj ) 02001 { 02002 $this->Operators[$op_name] = $op_obj; 02003 } 02004 02005 /*! 02006 Unregisters the operator $op_name. 02007 */ 02008 function unregisterOperator( $op_name ) 02009 { 02010 if ( is_array( $op_name ) ) 02011 { 02012 foreach ( $op_name as $op ) 02013 { 02014 $this->unregisterOperator( $op_name ); 02015 } 02016 } 02017 else if ( isset( $this->Operators ) ) 02018 unset( $this->Operators[$op_name] ); 02019 else 02020 $this->warning( "unregisterOpearator()", "Operator $op_name is not registered, cannot unregister" ); 02021 } 02022 02023 /*! 02024 Not implemented yet. 02025 */ 02026 function registerFilter() 02027 { 02028 } 02029 02030 /*! 02031 Registers a new resource object $res. 02032 The resource object take care of fetching templates using an URI. 02033 */ 02034 function registerResource( $res ) 02035 { 02036 if ( is_object( $res ) ) 02037 $this->Resources[$res->resourceName()] =& $res; 02038 else 02039 $this->warning( "registerResource()", "Supplied argument is not a resource object" ); 02040 } 02041 02042 /*! 02043 Unregisters the resource $res_name. 02044 */ 02045 function unregisterResource( $res_name ) 02046 { 02047 if ( is_array( $res_name ) ) 02048 { 02049 foreach ( $res_name as $res ) 02050 { 02051 $this->unregisterResource( $res ); 02052 } 02053 } 02054 else if ( isset( $this->Resources[$res_name] ) ) 02055 unset( $this->Resources[$res_name] ); 02056 else 02057 $this->warning( "unregisterResource()", "Resource $res_name is not registered, cannot unregister" ); 02058 } 02059 02060 /*! 02061 Sets whether detail output is used or not. 02062 Detail output is useful for debug output where you want to examine the template 02063 and the output text. 02064 */ 02065 function setShowDetails( $show ) 02066 { 02067 $this->ShowDetails = $show; 02068 } 02069 02070 /*! 02071 Outputs a warning about the parameter $param missing for function/operator $name. 02072 */ 02073 function missingParameter( $name, $param ) 02074 { 02075 $this->warning( $name, "Missing parameter $param" ); 02076 } 02077 02078 /*! 02079 Outputs a warning about the parameter count being to high for function/operator $name. 02080 */ 02081 function extraParameters( $name, $count, $maxCount ) 02082 { 02083 $this->warning( $name, "Passed $count parameters but correct count is $maxCount" ); 02084 } 02085 02086 /*! 02087 Outputs a warning about the variable $var being undefined. 02088 */ 02089 function undefinedVariable( $name, $var ) 02090 { 02091 $this->warning( $name, "Undefined variable: $var" ); 02092 } 02093 02094 /*! 02095 Outputs an error about the template function $func_name being undefined. 02096 */ 02097 function undefinedFunction( $func_name ) 02098 { 02099 $this->error( "", "Undefined function: $func_name" ); 02100 } 02101 02102 /*! 02103 Creates a string for the placement information and returns it. 02104 \note The placement information can either be in indexed or associative 02105 */ 02106 function placementText( $placement = false ) 02107 { 02108 $placementText = false; 02109 if ( $placement !== false ) 02110 { 02111 if ( isset( $placement['start'] ) and 02112 isset( $placement['stop'] ) and 02113 isset( $placement['templatefile'] ) ) 02114 { 02115 $line = $placement['start']['line']; 02116 $column = $placement['start']['column']; 02117 $templateFile = $placement['templatefile']; 02118 } 02119 else 02120 { 02121 $line = $placement[0][0]; 02122 $column = $placement[0][1]; 02123 $templateFile = $placement[2]; 02124 } 02125 02126 $placementText = " @ $templateFile:$line" . "[$column]"; 02127 } 02128 return $placementText; 02129 } 02130 02131 /*! 02132 Displays a warning for the function/operator $name and text $txt. 02133 */ 02134 function warning( $name, $txt, $placement = false ) 02135 { 02136 $this->WarningLog[] = array( 'name' => $name, 02137 'text' => $txt, 02138 'placement' => $placement ); 02139 02140 if ( !is_string( $placement ) ) 02141 $placementText = $this->placementText( $placement ); 02142 else 02143 $placementText = $placement; 02144 $placementText = $this->placementText( $placement ); 02145 if ( $name != "" ) 02146 eZDebug::writeWarning( $txt, "eZTemplate:$name" . $placementText ); 02147 else 02148 eZDebug::writeWarning( $txt, "eZTemplate" . $placementText ); 02149 } 02150 02151 /*! 02152 Displays an error for the function/operator $name and text $txt. 02153 */ 02154 function error( $name, $txt, $placement = false ) 02155 { 02156 $this->ErrorLog[] = array( 'name' => $name, 02157 'text' => $txt, 02158 'placement' => $placement ); 02159 02160 if ( !is_string( $placement ) ) 02161 $placementText = $this->placementText( $placement ); 02162 else 02163 $placementText = $placement; 02164 if ( $name != "" ) 02165 $nameText = "eZTemplate:$name"; 02166 else 02167 $nameText = "eZTemplate"; 02168 eZDebug::writeError( $txt, $nameText . $placementText ); 02169 $hasAppendWarning =& $GLOBALS['eZTemplateHasAppendWarning']; 02170 $ini = $this->ini(); 02171 if ( $ini->variable( 'ControlSettings', 'DisplayWarnings' ) == 'enabled' ) 02172 { 02173 if ( !isset( $hasAppendWarning ) or 02174 !$hasAppendWarning ) 02175 { 02176 if ( function_exists( 'eZAppendWarningItem' ) ) 02177 { 02178 eZAppendWarningItem( array( 'error' => array( 'type' => 'template', 02179 'number' => eZTemplate::FILE_ERRORS ), 02180 'text' => ezpI18n::tr( 'lib/eztemplate', 'Some template errors occurred, see debug for more information.' ) ) ); 02181 $hasAppendWarning = true; 02182 } 02183 } 02184 } 02185 } 02186 02187 02188 function operatorInputSupported( $operatorName ) 02189 { 02190 } 02191 02192 /*! 02193 Sets the original text for uri $uri to $text. 02194 */ 02195 function setIncludeText( $uri, $text ) 02196 { 02197 $this->IncludeText[$uri] = $text; 02198 } 02199 02200 /*! 02201 Sets the output for uri $uri to $output. 02202 */ 02203 function setIncludeOutput( $uri, $output ) 02204 { 02205 $this->IncludeOutput[$uri] = $output; 02206 } 02207 02208 /*! 02209 \return the path list which is used for autoloading functions and operators. 02210 */ 02211 function autoloadPathList() 02212 { 02213 return $this->AutoloadPathList; 02214 } 02215 02216 /*! 02217 Sets the path list for autoloading. 02218 */ 02219 function setAutoloadPathList( $pathList ) 02220 { 02221 $this->AutoloadPathList = $pathList; 02222 } 02223 02224 /*! 02225 Looks trough the pathes specified in autoloadPathList() and fetches autoload 02226 definition files used for autoloading functions and operators. 02227 */ 02228 function autoload() 02229 { 02230 $pathList = $this->autoloadPathList(); 02231 foreach ( $pathList as $path ) 02232 { 02233 $autoloadFile = $path . 'eztemplateautoload.php'; 02234 if ( file_exists( $autoloadFile ) ) 02235 { 02236 unset( $eZTemplateOperatorArray ); 02237 unset( $eZTemplateFunctionArray ); 02238 include( $autoloadFile ); 02239 if ( isset( $eZTemplateOperatorArray ) && 02240 is_array( $eZTemplateOperatorArray ) ) 02241 { 02242 foreach ( $eZTemplateOperatorArray as $operatorDefinition ) 02243 { 02244 $this->registerAutoloadOperators( $operatorDefinition ); 02245 } 02246 } 02247 if ( isset( $eZTemplateFunctionArray ) && 02248 is_array( $eZTemplateFunctionArray ) ) 02249 { 02250 foreach ( $eZTemplateFunctionArray as $functionDefinition ) 02251 { 02252 $this->registerAutoloadFunctions( $functionDefinition ); 02253 } 02254 } 02255 } 02256 else 02257 { 02258 eZDebug::writeWarning( "Path '$path' does not have the file 'eztemplateautoload.php' allthough it reported it had one.\n" . 02259 "Looked for file '" . $autoloadFile . "'\n" . 02260 "Check the setting [TemplateSettings]/ExtensionAutoloadPath or AutoloadPathList in your site.ini settings." ); 02261 } 02262 } 02263 } 02264 02265 /*! 02266 Resets all template variables. 02267 */ 02268 function resetVariables() 02269 { 02270 $this->Variables = array(); 02271 } 02272 02273 /*! 02274 Resets all template functions and operators by calling the resetFunction and resetOperator 02275 on all elements that supports it. 02276 */ 02277 function resetElements() 02278 { 02279 foreach ( $this->Functions as $functionName => $functionObject ) 02280 { 02281 if ( is_object( $functionObject ) and 02282 method_exists( $functionObject, 'resetFunction' ) ) 02283 { 02284 $functionObject->resetFunction( $functionName ); 02285 } 02286 } 02287 02288 foreach ( $this->Operators as $operatorName => $operatorObject ) 02289 { 02290 if ( is_object( $operatorObject ) and 02291 method_exists( $operatorObject, 'resetOperator' ) ) 02292 { 02293 $operatorObject->resetOperator( $operatorName ); 02294 } 02295 } 02296 } 02297 02298 /*! 02299 Resets all template variables, functions, operators and error counts. 02300 */ 02301 function reset() 02302 { 02303 $this->resetVariables(); 02304 $this->resetElements(); 02305 $this->IsCachingAllowed = true; 02306 02307 $this->resetErrorLog(); 02308 02309 $this->TemplatesUsageStatistics = array(); 02310 $this->TemplateFetchList = array(); 02311 } 02312 02313 /*! 02314 \return The number of errors that occured with the last fetch 02315 \sa hasErrors() 02316 */ 02317 function errorCount() 02318 { 02319 return count( $this->ErrorLog ); 02320 } 02321 02322 /*! 02323 \return \ true if errors occured with the last fetch. 02324 \sa errorCount() 02325 */ 02326 function hasErrors() 02327 { 02328 return $this->errorCount() > 0; 02329 } 02330 02331 /*! 02332 \return error log. 02333 \sa errorCount() 02334 */ 02335 function errorLog() 02336 { 02337 return $this->ErrorLog; 02338 } 02339 02340 /*! 02341 \return The number of warnings that occured with the last fetch 02342 \sa hasWarnings() 02343 */ 02344 function warningCount() 02345 { 02346 return count( $this->WarningLog ); 02347 } 02348 02349 /*! 02350 \return \ true if warnings occured with the last fetch. 02351 \sa warningCount() 02352 */ 02353 function hasWarnings() 02354 { 02355 return $this->warningCount() > 0; 02356 } 02357 02358 /*! 02359 \return waring log. 02360 \sa warningCount() 02361 */ 02362 function warningLog() 02363 { 02364 return $this->WarningLog; 02365 } 02366 02367 /** 02368 * Returns a shared instance of the eZTemplate class. 02369 * 02370 * @return eZTemplate 02371 */ 02372 public static function instance() 02373 { 02374 if ( self::$instance === null ) 02375 { 02376 self::$instance = new eZTemplate(); 02377 } 02378 return self::$instance; 02379 } 02380 02381 /** 02382 * Returns a shared instance of the eZTemplate class with 02383 * default settings applied, like: 02384 * - Autoload operators loaded 02385 * - Debug mode set 02386 * - eZTemplateDesignResource::instance registered 02387 * 02388 * @since 4.3 02389 * @return eZTemplate 02390 */ 02391 public static function factory() 02392 { 02393 if ( self::$factory === false ) 02394 { 02395 $instance = self::instance(); 02396 02397 $ini = eZINI::instance(); 02398 if (!isset($GLOBALS['eZTemplateDebugInternalsEnabled']) && $ini->variable( 'TemplateSettings', 'Debug' ) == 'enabled' ) 02399 eZTemplate::setIsDebugEnabled( true ); 02400 02401 $compatAutoLoadPath = $ini->variableArray( 'TemplateSettings', 'AutoloadPath' ); 02402 $autoLoadPathList = $ini->variable( 'TemplateSettings', 'AutoloadPathList' ); 02403 02404 $extensionAutoloadPath = $ini->variable( 'TemplateSettings', 'ExtensionAutoloadPath' ); 02405 $extensionPathList = eZExtension::expandedPathList( $extensionAutoloadPath, 'autoloads/' ); 02406 02407 $autoLoadPathList = array_unique( array_merge( $compatAutoLoadPath, $autoLoadPathList, $extensionPathList ) ); 02408 02409 $instance->setAutoloadPathList( $autoLoadPathList ); 02410 $instance->autoload(); 02411 02412 $instance->registerResource( eZTemplateDesignResource::instance() ); 02413 self::$factory = true; 02414 } 02415 return self::instance(); 02416 } 02417 02418 /** 02419 * Reset shared instance of the eZTemplate class and factory flag 02420 * as used by {@link eZTemplate::instance()} and {@link eZTemplate::factory()} 02421 * 02422 * @since 4.3 02423 */ 02424 public static function resetInstance() 02425 { 02426 self::$instance = null; 02427 self::$factory = false; 02428 } 02429 02430 /** 02431 * Returns the eZINI object instance for the template.ini file. 02432 * 02433 * @return eZINI 02434 */ 02435 public function ini() 02436 { 02437 return eZINI::instance( "template.ini" ); 02438 } 02439 02440 /*! 02441 \static 02442 \return true if special XHTML code should be included before the included template file. 02443 This code will display the template filename in the browser but will eventually 02444 break the design. 02445 */ 02446 static function isXHTMLCodeIncluded() 02447 { 02448 if ( !isset( $GLOBALS['eZTemplateDebugXHTMLCodeEnabled'] ) ) 02449 { 02450 $ini = eZINI::instance(); 02451 $GLOBALS['eZTemplateDebugXHTMLCodeEnabled'] = $ini->variable( 'TemplateSettings', 'ShowXHTMLCode' ) == 'enabled'; 02452 } 02453 return $GLOBALS['eZTemplateDebugXHTMLCodeEnabled']; 02454 } 02455 02456 /*! 02457 \static 02458 \return \c true if debug output of template functions and operators should be enabled. 02459 */ 02460 static function isMethodDebugEnabled() 02461 { 02462 if ( !isset( $GLOBALS['eZTemplateDebugMethodEnabled'] ) ) 02463 { 02464 $ini = eZINI::instance(); 02465 $GLOBALS['eZTemplateDebugMethodEnabled'] = $ini->variable( 'TemplateSettings', 'ShowMethodDebug' ) == 'enabled'; 02466 } 02467 return $GLOBALS['eZTemplateDebugMethodEnabled']; 02468 } 02469 02470 /*! 02471 \static 02472 \return true if debugging of internals is enabled, this will display 02473 which files are loaded and when cache files are created. 02474 Set the option with setIsDebugEnabled(). 02475 */ 02476 static function isDebugEnabled() 02477 { 02478 if ( !isset( $GLOBALS['eZTemplateDebugInternalsEnabled'] ) ) 02479 $GLOBALS['eZTemplateDebugInternalsEnabled'] = eZTemplate::DEBUG_INTERNALS; 02480 return $GLOBALS['eZTemplateDebugInternalsEnabled']; 02481 } 02482 02483 /*! 02484 \static 02485 Sets whether internal debugging is enabled or not. 02486 */ 02487 static function setIsDebugEnabled( $debug ) 02488 { 02489 $GLOBALS['eZTemplateDebugInternalsEnabled'] = $debug; 02490 } 02491 02492 /*! 02493 \return \c true if caching is allowed (default) or \c false otherwise. 02494 This also affects template compiling. 02495 \sa setIsCachingAllowed 02496 */ 02497 function isCachingAllowed() 02498 { 02499 return $this->IsCachingAllowed; 02500 } 02501 02502 /*! 02503 Sets whether caching/compiling is allowed or not. This is useful 02504 if you need to make sure templates are parsed and processed 02505 without any caching mechanisms. 02506 \note The default is to allow caching. 02507 \sa isCachingAllowed 02508 */ 02509 function setIsCachingAllowed( $allowed ) 02510 { 02511 $this->IsCachingAllowed = $allowed; 02512 } 02513 02514 /*! 02515 \static 02516 \return \c true if templates usage statistics should be enabled. 02517 */ 02518 static function isTemplatesUsageStatisticsEnabled() 02519 { 02520 if ( !isset( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] ) ) 02521 { 02522 $ini = eZINI::instance(); 02523 $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] = $ini->variable( 'TemplateSettings', 'ShowUsedTemplates' ) == 'enabled'; 02524 } 02525 return ( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] ); 02526 } 02527 02528 /*! 02529 \static 02530 Sets whether templates usage statistics enabled or not. 02531 \return \c true if templates usage statistics was enabled, otherwise \c false. 02532 */ 02533 function setIsTemplatesUsageStatisticsEnabled( $enabled ) 02534 { 02535 $wasEnabled = false; 02536 if( isset( $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] ) ) 02537 $wasEnabled = $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled']; 02538 02539 $GLOBALS['eZTemplateDebugTemplatesUsageStatisticsEnabled'] = $enabled; 02540 return $wasEnabled; 02541 } 02542 02543 /*! 02544 \static 02545 Checks settings and if 'ShowUsedTemplates' is enabled appends template info to stats. 02546 */ 02547 function appendTemplateToStatisticsIfNeeded( &$templateName, &$templateFileName ) 02548 { 02549 if ( eZTemplate::isTemplatesUsageStatisticsEnabled() ) 02550 eZTemplate::appendTemplateToStatistics( $templateName, $templateFileName ); 02551 } 02552 02553 /*! 02554 \static 02555 Appends template info to stats. 02556 */ 02557 function appendTemplateToStatistics( $templateName, $templateFileName ) 02558 { 02559 $actualTemplateName = preg_replace( "#^[\w/]+templates/#", '', $templateFileName ); 02560 $requestedTemplateName = preg_replace( "#^[\w/]+templates/#", '', $templateName ); 02561 02562 $tpl = eZTemplate::instance(); 02563 $needToAppend = true; 02564 02565 // don't add template info if it is a duplicate of previous. 02566 $statsSize = count( $tpl->TemplatesUsageStatistics ); 02567 if ( $statsSize > 0 ) 02568 { 02569 $lastTemplateInfo = $tpl->TemplatesUsageStatistics[$statsSize-1]; 02570 if ( $lastTemplateInfo['actual-template-name'] === $actualTemplateName && 02571 $lastTemplateInfo['requested-template-name'] === $requestedTemplateName && 02572 $lastTemplateInfo['template-filename'] === $templateFileName ) 02573 { 02574 $needToAppend = false; 02575 } 02576 } 02577 02578 if ( $needToAppend ) 02579 { 02580 $templateInfo = array( 'actual-template-name' => $actualTemplateName, 02581 'requested-template-name' => $requestedTemplateName, 02582 'template-filename' => $templateFileName ); 02583 02584 $tpl->TemplatesUsageStatistics[] = $templateInfo; 02585 } 02586 } 02587 02588 /*! 02589 Appends template info for current fetch. 02590 */ 02591 function appendTemplateFetch( $actualTemplateName ) 02592 { 02593 $this->TemplateFetchList[] = $actualTemplateName; 02594 $this->TemplateFetchList = array_unique( $this->TemplateFetchList ); 02595 } 02596 02597 /*! 02598 Reset error and warning logs 02599 */ 02600 function resetErrorLog() 02601 { 02602 $this->ErrorLog = array(); 02603 $this->WarningLog = array(); 02604 } 02605 02606 /*! 02607 \static 02608 Returns template usage statistics 02609 */ 02610 static function templatesUsageStatistics() 02611 { 02612 $tpl = eZTemplate::instance(); 02613 return $tpl->TemplatesUsageStatistics; 02614 } 02615 02616 /*! 02617 Returns template list for the last fetch. 02618 */ 02619 function templateFetchList() 02620 { 02621 return $this->TemplateFetchList; 02622 } 02623 02624 /*! 02625 Set template compilation test mode. 02626 02627 \param true, will set template compilation in test mode ( no disc writes ). 02628 false, will compile templates to disc 02629 */ 02630 function setCompileTest( $val ) 02631 { 02632 $this->TestCompile = $val; 02633 } 02634 02635 /*! 02636 Get if template session is test compile 02637 */ 02638 function testCompile() 02639 { 02640 return $this->TestCompile; 02641 } 02642 02643 /// \privatesection 02644 /// Associative array of resource objects 02645 public $Resources; 02646 /// Reference to the default resource object 02647 public $DefaultResource; 02648 /// The original template text 02649 public $Text; 02650 /// Included texts, usually performed by custom functions 02651 public $IncludeText; 02652 /// Included outputs, usually performed by custom functions 02653 public $IncludeOutput; 02654 /// The timestamp of the template when it was last modified 02655 public $TimeStamp; 02656 /// The left delimiter used for parsing 02657 public $LDelim; 02658 /// The right delimiter used for parsing 02659 public $RDelim; 02660 02661 /// The resulting object tree of the template 02662 public $Tree; 02663 /// An associative array of template variables 02664 public $Variables; 02665 /*! 02666 Last element of this stack contains names of 02667 all variables created in the innermost template, for them 02668 to be destroyed after the template execution finishes. 02669 */ 02670 public $LocalVariablesNamesStack; 02671 // Reference to the last element of $LocalVariablesNamesStack. 02672 public $CurrentLocalVariablesNames; 02673 /// An associative array of operators 02674 public $Operators; 02675 /// An associative array of functions 02676 public $Functions; 02677 /// An associative array of function attributes 02678 public $FunctionAttributes; 02679 /// An associative array of literal tags 02680 public $Literals; 02681 /// True if output details is to be shown 02682 public $ShowDetails = false; 02683 /// \c true if caching is allowed 02684 public $IsCachingAllowed; 02685 02686 /// Array containing all errors occured during a fetch 02687 public $ErrorLog; 02688 /// Array containing all warnings occured during a fetch 02689 public $WarningLog; 02690 02691 public $AutoloadPathList; 02692 /// include level 02693 public $Level = 0; 02694 public $MaxLevel = 40; 02695 02696 /// A list of templates used by a rendered page 02697 public $TemplatesUsageStatistics; 02698 02699 // counter to make unique names for {foreach} loop variables in com 02700 public $ForeachCounter; 02701 public $ForCounter; 02702 public $WhileCounter; 02703 public $DoCounter; 02704 public $ElseifCounter; 02705 02706 // Flag for setting compilation in test mode 02707 public $TestCompile; 02708 02709 /** 02710 * Singelton instance of eZTemplate used by {@link eZTemplate::instance()} 02711 * Reset with {@link eZTemplate::resetInstance()} 02712 * 02713 * @var null|eZTemplate 02714 */ 02715 protected static $instance; 02716 02717 /** 02718 * Factory flag as used by {@link eZTemplate::factory()} 02719 * Reset with {@link eZTemplate::resetInstance()} 02720 * 02721 * @var bool 02722 */ 02723 protected static $factory = false; 02724 02725 // public $CurrentRelatedResource; 02726 // public $CurrentRelatedTemplateName; 02727 } 02728 02729 ?>