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