PageRenderTime 68ms CodeModel.GetById 10ms app.highlight 31ms RepoModel.GetById 12ms app.codeStats 1ms

/lib/eztemplate/classes/eztemplate.php

https://github.com/StephanBoganskyXrow/ezpublish
PHP | 2707 lines | 1820 code | 229 blank | 658 comment | 262 complexity | df4ebd9eaf52cf3767eaaaff2330b425 MD5 | raw file

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

   1<?php
   2//
   3// Definition of eZTemplate class
   4//
   5// Created on: <01-Mar-2002 13:49:57 amos>
   6//
   7// ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
   8// SOFTWARE NAME: eZ Publish
   9// SOFTWARE RELEASE: 4.1.x
  10// COPYRIGHT NOTICE: Copyright (C) 1999-2011 eZ Systems AS
  11// SOFTWARE LICENSE: GNU General Public License v2.0
  12// NOTICE: >
  13//   This program is free software; you can redistribute it and/or
  14//   modify it under the terms of version 2.0  of the GNU General
  15//   Public License as published by the Free Software Foundation.
  16//
  17//   This program is distributed in the hope that it will be useful,
  18//   but WITHOUT ANY WARRANTY; without even the implied warranty of
  19//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20//   GNU General Public License for more details.
  21//
  22//   You should have received a copy of version 2.0 of the GNU General
  23//   Public License along with this program; if not, write to the Free
  24//   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  25//   MA 02110-1301, USA.
  26//
  27//
  28// ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
  29//
  30
  31/*! \file
  32 Template system manager.
  33*/
  34
  35/*! \defgroup eZTemplate Template system */
  36
  37/*!
  38  \class eZTemplate eztemplate.php
  39  \ingroup eZTemplate
  40  \brief The main manager for templates
  41
  42  The template systems allows for separation of code and
  43  layout by moving the layout part into template files. These
  44  template files are parsed and processed with template variables set
  45  by the PHP code.
  46
  47  The template system in itself is does not do much, it parses template files
  48  according to a rule set sets up a tree hierarchy and process the data
  49  using functions and operators. The standard template system comes with only
  50  a few functions and no operators, it is meant for these functions and operators
  51  to be specified by the users of the template system. But for simplicity a few
  52  help classes is available which can be easily enabled.
  53
  54  The classes are:
  55  - eZTemplateDelimitFunction - Inserts the left and right delimiter which are normally parsed.
  56  - eZTemplateSectionFunction - Allows for conditional blocks and loops.
  57  - eZTemplateIncludeFunction - Includes external templates
  58  - eZTemplateSequenceFunction - Creates sequences arrays
  59  - eZTemplateSwitchFunction - Conditional output of template
  60
  61  - eZTemplatePHPOperator - Allows for easy redirection of operator names to PHP functions.
  62  - eZTemplateLocaleOperator - Allows for locale conversions.
  63  - eZTemplateArrayOperator - Creates arrays
  64  - eZTemplateAttributeOperator - Displays contents of template variables, useful for debugging
  65  - eZTemplateImageOperator - Converts text to image
  66  - eZTemplateLogicOperator - Various logical operators for boolean handling
  67  - eZTemplateUnitOperator - Unit conversion and display
  68
  69  To enable these functions and operator use registerFunction and registerOperator.
  70
  71  In keeping with the spirit of being simple the template system does not know how
  72  to get the template files itself. Instead it relies on resource handlers, these
  73  handlers fetches the template files using different kind of transport mechanism.
  74  For simplicity a default resource class is available, eZTemplateFileResource fetches
  75  templates from the filesystem.
  76
  77  The parser process consists of three passes, each pass adds a new level of complexity.
  78  The first pass strips text from template blocks which starts with a left delimiter and
  79  ends with a right delimiter (default is { and } ), and places them in an array.
  80  The second pass iterates the text and block elements and removes newlines from
  81  text before function blocks and text after function blocks.
  82  The third pass builds the tree according the function rules.
  83
  84  Processing is done by iterating over the root of the tree, if a text block is found
  85  the text is appended to the result text. If a variable or contant is it's data is extracted
  86  and any operators found are run on it before fetching the result and appending it to
  87  the result text. If a function is found the function is called with the parameters
  88  and it's up to the function handle children if any.
  89
  90  Constants and template variables will usually be called variables since there's little
  91  difference. A template variable expression will start with a $ and consists of a
  92  namespace (optional) a name and attribues(optional). The variable expression
  93  \verbatim $root:var.attr1 \endverbatim exists in the "root" namespace, has the name "var" and uses the
  94  attribute "attr1". Some functions will create variables on demand, to avoid name conflicts
  95  namespaces were introduced, each function will place the new variables in a namespace
  96  specified in the template file. Attribues are used for fetching parts of the variable,
  97  for instance an element in an array or data in an object. Since the syntax is the
  98  same for arrays and objects the PHP code can use simple arrays when speed is required,
  99  the template code will not care.
 100  A different syntax is also available when you want to access an attribute using a variable.
 101  For instance \verbatim $root:var[$attr_var] \endverbatim, if the variable $attr_var contains "attr1" it would
 102  access the same attribute as in the first example.
 103
 104  The syntax for operators is a | and a name, optionally parameters can be specified with
 105  ( and ) delimited with ,. Valid operators are \verbatim |upcase, |l10n(date) \endverbatim.
 106
 107  Functions look a lot like HTML/XML tags. The function consists of a name and parameters
 108  which are assigned using the param=value syntax. Some parameters may be required while
 109  others may be optionally, the exact behaviour is specified by each function.
 110  Valid functions are \verbatim "section name=abc loop=4" \endverbatim
 111
 112  Example of usage:
 113\code
 114// Init template
 115$tpl = eZTemplate::instance();
 116
 117$tpl->registerOperators( new eZTemplatePHPOperator( array( "upcase" => "strtoupper",
 118                                                           "reverse" => "strrev" ) ) );
 119$tpl->registerOperators( new eZTemplateLocaleOperator() );
 120$tpl->registerFunction( "section", new eZTemplateSectionFunction( "section" ) );
 121$tpl->registerFunctions( new eZTemplateDelimitFunction() );
 122
 123$tpl->setVariable( "my_var", "{this value set by variable}", "test" );
 124$tpl->setVariable( "my_arr", array( "1st", "2nd", "third", "fjerde" ) );
 125$tpl->setVariable( "multidim", array( array( "a", "b" ),
 126                                      array( "c", "d" ),
 127                                      array( "e", "f" ),
 128                                      array( "g", "h" ) ) );
 129
 130class mytest
 131{
 132    function mytest( $n, $s )
 133    {
 134        $this->n = $n;
 135        $this->s = $s;
 136    }
 137
 138    function hasAttribute( $attr )
 139    {
 140        return ( $attr == "name" || $attr == "size" );
 141    }
 142
 143    function attribute( $attr )
 144    {
 145        switch ( $attr )
 146        {
 147            case "name";
 148                return $this->n;
 149            case "size";
 150                return $this->s;
 151            default:
 152                $retAttr = null;
 153                return $retAttr;
 154        }
 155    }
 156
 157}
 158
 159$tpl->setVariable( "multidim_obj", array( new mytest( "jan", 200 ),
 160                                          new mytest( "feb", 200 ),
 161                                          new mytest( "john", 200 ),
 162                                          new mytest( "doe", 50 ) ) );
 163$tpl->setVariable( "curdate", time() );
 164
 165$tpl->display( "lib/eztemplate/example/test.tpl" );
 166
 167// test.tpl
 168
 169{section name=outer loop=4}
 170123
 171{delimit}::{/delimit}
 172{/section}
 173
 174{literal test=1} This is some {blah arg1="" arg2="abc" /} {/literal}
 175
 176<title>This is a test</title>
 177<table border="1">
 178<tr><th>{$test:my_var}
 179{"some text!!!"|upcase|reverse}</th></tr>
 180{section name=abc loop=$my_arr}
 181<tr><td>{$abc:item}</td></tr>
 182{/section}
 183</table>
 184
 185<table border="1">
 186{section name=outer loop=$multidim}
 187<tr>
 188{section name=inner loop=$outer:item}
 189<td>{$inner:item}</td>
 190{/section}
 191</tr>
 192{/section}
 193</table>
 194
 195<table border="1">
 196{section name=outer loop=$multidim_obj}
 197<tr>
 198<td>{$outer:item.name}</td>
 199<td>{$outer:item.size}</td>
 200</tr>
 201{/section}
 202</table>
 203
 204{section name=outer loop=$nonexistingvar}
 205<b><i>Dette skal ikke vises</b></i>
 206{section-else}
 207<b><i>This is shown when the {ldelim}$loop{rdelim} variable is non-existant</b></i>
 208{/section}
 209
 210
 211Denne koster {1.4|l10n(currency)}<br>
 212{-123456789|l10n(number)}<br>
 213{$curdate|l10n(date)}<br>
 214{$curdate|l10n(shortdate)}<br>
 215{$curdate|l10n(time)}<br>
 216{$curdate|l10n(shorttime)}<br>
 217{include file="test2.tpl"/}
 218
 219\endcode
 220*/
 221
 222class eZTemplate
 223{
 224    const RESOURCE_FETCH = 1;
 225    const RESOURCE_QUERY = 2;
 226
 227    const ELEMENT_TEXT = 1;
 228    const ELEMENT_SINGLE_TAG = 2;
 229    const ELEMENT_NORMAL_TAG = 3;
 230    const ELEMENT_END_TAG = 4;
 231    const ELEMENT_VARIABLE = 5;
 232    const ELEMENT_COMMENT = 6;
 233
 234    const NODE_ROOT = 1;
 235    const NODE_TEXT = 2;
 236    const NODE_VARIABLE = 3;
 237    const NODE_FUNCTION = 4;
 238    const NODE_OPERATOR = 5;
 239
 240
 241    const NODE_INTERNAL = 100;
 242    const NODE_INTERNAL_CODE_PIECE = 101;
 243
 244    const NODE_INTERNAL_VARIABLE_SET = 105;
 245    const NODE_INTERNAL_VARIABLE_UNSET = 102;
 246
 247    const NODE_INTERNAL_NAMESPACE_CHANGE = 103;
 248    const NODE_INTERNAL_NAMESPACE_RESTORE = 104;
 249
 250    const NODE_INTERNAL_WARNING = 120;
 251    const NODE_INTERNAL_ERROR = 121;
 252
 253    const NODE_INTERNAL_RESOURCE_ACQUISITION = 140;
 254    const NODE_OPTIMIZED_RESOURCE_ACQUISITION = 141;
 255
 256    const NODE_INTERNAL_OUTPUT_ASSIGN = 150;
 257    const NODE_INTERNAL_OUTPUT_READ = 151;
 258    const NODE_INTERNAL_OUTPUT_INCREASE = 152;
 259    const NODE_INTERNAL_OUTPUT_DECREASE = 153;
 260
 261    const NODE_INTERNAL_OUTPUT_SPACING_INCREASE = 160;
 262    const NODE_INTERNAL_SPACING_DECREASE = 161;
 263
 264    const NODE_OPTIMIZED_INIT = 201;
 265
 266
 267    const NODE_USER_CUSTOM = 1000;
 268
 269
 270    const TYPE_VOID = 0;
 271    const TYPE_STRING = 1;
 272    const TYPE_NUMERIC = 2;
 273    const TYPE_IDENTIFIER = 3;
 274    const TYPE_VARIABLE = 4;
 275    const TYPE_ATTRIBUTE = 5;
 276    const TYPE_OPERATOR = 6;
 277    const TYPE_BOOLEAN = 7;
 278    const TYPE_ARRAY = 8;
 279    const TYPE_DYNAMIC_ARRAY = 9;
 280
 281    const TYPE_INTERNAL = 100;
 282    const TYPE_INTERNAL_CODE_PIECE = 101;
 283    const TYPE_PHP_VARIABLE = 102;
 284
 285    const TYPE_OPTIMIZED_NODE = 201;
 286    const TYPE_OPTIMIZED_ARRAY_LOOKUP = 202;
 287    const TYPE_OPTIMIZED_CONTENT_CALL = 203;
 288    const TYPE_OPTIMIZED_ATTRIBUTE_LOOKUP = 204;
 289
 290    const TYPE_INTERNAL_STOP = 999;
 291
 292
 293    const TYPE_STRING_BIT = 1;
 294    const TYPE_NUMERIC_BIT = 2;
 295    const TYPE_IDENTIFIER_BIT = 4;
 296    const TYPE_VARIABLE_BIT = 8;
 297    const TYPE_ATTRIBUTE_BIT = 16;
 298    const TYPE_OPERATOR_BIT = 32;
 299
 300    const TYPE_NONE = 0;
 301
 302    const TYPE_ALL = 63;
 303
 304    const TYPE_BASIC = 47;
 305
 306    const TYPE_MODIFIER_MASK = 48;
 307
 308    const NAMESPACE_SCOPE_GLOBAL = 1;
 309    const NAMESPACE_SCOPE_LOCAL = 2;
 310    const NAMESPACE_SCOPE_RELATIVE = 3;
 311
 312    const DEBUG_INTERNALS = false;
 313
 314    const FILE_ERRORS = 1;
 315
 316    /*!
 317     Intializes the template with left and right delimiters being { and },
 318     and a file resource. The literal tag "literal" is also registered.
 319    */
 320    function eZTemplate()
 321    {
 322        $this->Tree = array( eZTemplate::NODE_ROOT, false );
 323        $this->LDelim = "{";
 324        $this->RDelim = "}";
 325
 326        $this->IncludeText = array();
 327        $this->IncludeOutput = array();
 328
 329        $this->registerLiteral( "literal" );
 330
 331        $res = new eZTemplateFileResource();
 332        $this->DefaultResource = $res;
 333        $this->registerResource( $res );
 334
 335        $this->Resources = array();
 336        $this->Text = null;
 337
 338        $this->IsCachingAllowed = true;
 339
 340        $this->resetErrorLog();
 341
 342        $this->AutoloadPathList = array( 'lib/eztemplate/classes/' );
 343        $this->Variables = array();
 344        $this->LocalVariablesNamesStack = array();
 345        $this->CurrentLocalVariablesNames = null;
 346        $this->Functions = array();
 347        $this->FunctionAttributes = array();
 348
 349        $this->TestCompile = false;
 350
 351        $ini = eZINI::instance( 'template.ini' );
 352        if ( $ini->hasVariable( 'ControlSettings', 'MaxLevel' ) )
 353             $this->MaxLevel = $ini->variable( 'ControlSettings', 'MaxLevel' );
 354
 355        $this->MaxLevelWarning = ezpI18n::tr( 'lib/template',
 356                                         'The maximum nesting level of %max has been reached. The execution is stopped to avoid infinite recursion.',
 357                                         '',
 358                                         array( '%max' => $this->MaxLevel ) );
 359        eZDebug::createAccumulatorGroup( 'template_total', 'Template Total' );
 360
 361        $this->TemplatesUsageStatistics = array();
 362        // Array of templates which are used in a single fetch()
 363        $this->TemplateFetchList = array();
 364
 365        $this->ForeachCounter = 0;
 366        $this->ForCounter     = 0;
 367        $this->WhileCounter   = 0;
 368        $this->DoCounter      = 0;
 369        $this->ElseifCounter  = 0;
 370    }
 371
 372    /*!
 373     Returns the left delimiter being used.
 374    */
 375    function leftDelimiter()
 376    {
 377        return $this->LDelim;
 378    }
 379
 380    /*!
 381     Returns the right delimiter being used.
 382    */
 383    function rightDelimiter()
 384    {
 385        return $this->RDelim;
 386    }
 387
 388    /*!
 389     Sets the left delimiter.
 390    */
 391    function setLeftDelimiter( $delim )
 392    {
 393        $this->LDelim = $delim;
 394    }
 395
 396    /*!
 397     Sets the right delimiter.
 398    */
 399    function setRightDelimiter( $delim )
 400    {
 401        $this->RDelim = $delim;
 402    }
 403
 404    /*!
 405     Fetches the result of the template file and displays it.
 406     If $template is supplied it will load this template file first.
 407    */
 408    function display( $template = false, $extraParameters = false )
 409    {
 410        $output = $this->fetch( $template, $extraParameters );
 411        if ( $this->ShowDetails )
 412        {
 413            echo '<h1>Result:</h1>' . "\n";
 414            echo '<hr/>' . "\n";
 415        }
 416        echo "$output";
 417        if ( $this->ShowDetails )
 418        {
 419            echo '<hr/>' . "\n";
 420        }
 421        if ( $this->ShowDetails )
 422        {
 423            echo "<h1>Template data:</h1>";
 424            echo "<p class=\"filename\">" . $template . "</p>";
 425            echo "<pre class=\"example\">" . htmlspecialchars( $this->Text ) . "</pre>";
 426            reset( $this->IncludeText );
 427            while ( ( $key = key( $this->IncludeText ) ) !== null )
 428            {
 429                $item = $this->IncludeText[$key];
 430                echo "<p class=\"filename\">" . $key . "</p>";
 431                echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>";
 432                next( $this->IncludeText );
 433            }
 434            echo "<h1>Result text:</h1>";
 435            echo "<p class=\"filename\">" . $template . "</p>";
 436            echo "<pre class=\"example\">" . htmlspecialchars( $output ) . "</pre>";
 437            reset( $this->IncludeOutput );
 438            while ( ( $key = key( $this->IncludeOutput ) ) !== null )
 439            {
 440                $item = $this->IncludeOutput[$key];
 441                echo "<p class=\"filename\">" . $key . "</p>";
 442                echo "<pre class=\"example\">" . htmlspecialchars( $item ) . "</pre>";
 443                next( $this->IncludeOutput );
 444            }
 445        }
 446    }
 447
 448    /*!
 449     * Initialize list of local variables for the current template.
 450     * The list contains only names of variables.
 451     */
 452    function createLocalVariablesList()
 453    {
 454        $this->LocalVariablesNamesStack[] = array();
 455        $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1];
 456    }
 457
 458    /*!
 459     * Check if the given local variable exists.
 460     */
 461    function hasLocalVariable( $varName, $rootNamespace )
 462    {
 463        return ( array_key_exists( $rootNamespace, $this->CurrentLocalVariablesNames ) &&
 464                 array_key_exists( $varName, $this->CurrentLocalVariablesNames[$rootNamespace] ) );
 465    }
 466
 467    /*!
 468     * Create a local variable.
 469     */
 470    function setLocalVariable( $varName, $varValue, $rootNamespace )
 471    {
 472        $this->CurrentLocalVariablesNames[$rootNamespace][$varName] = 1;
 473        $this->setVariable( $varName, $varValue, $rootNamespace );
 474    }
 475
 476    /*!
 477     * Destroy a local variable.
 478     */
 479    function unsetLocalVariable( $varName, $rootNamespace )
 480    {
 481        if ( !$this->hasLocalVariable( $varName, $rootNamespace ) )
 482            return;
 483
 484        $this->unsetVariable( $varName, $rootNamespace );
 485        unset( $this->CurrentLocalVariablesNames[$rootNamespace][$varName] );
 486    }
 487
 488    /*!
 489     * Destroy all local variables defined in the current template.
 490     */
 491    function unsetLocalVariables()
 492    {
 493        foreach ( $this->CurrentLocalVariablesNames as $ns => $vars )
 494        {
 495            foreach ( $vars as $var => $val )
 496                $this->unsetLocalVariable( $var, $ns );
 497        }
 498    }
 499
 500    /*!
 501     * Destroy list of local variables defined in the current (innermost) template.
 502     */
 503    function destroyLocalVariablesList()
 504    {
 505        array_pop( $this->LocalVariablesNamesStack );
 506
 507        if ( $this->LocalVariablesNamesStack )
 508            $this->CurrentLocalVariablesNames =& $this->LocalVariablesNamesStack[ count( $this->LocalVariablesNamesStack ) - 1];
 509        else
 510            unset( $this->CurrentLocalVariablesNames );
 511    }
 512
 513    /*!
 514     Tries to fetch the result of the template file and returns it.
 515     If $template is supplied it will load this template file first.
 516    */
 517    function fetch( $template = false, $extraParameters = false, $returnResourceData = false )
 518    {
 519        $this->resetErrorLog();
 520        // Reset fetch list when a new fetch is started
 521        $this->TemplateFetchList = array();
 522
 523        eZDebug::accumulatorStart( 'template_total' );
 524        eZDebug::accumulatorStart( 'template_load', 'template_total', 'Template load' );
 525        $root = null;
 526        if ( is_string( $template ) )
 527        {
 528            $resourceData = $this->loadURIRoot( $template, true, $extraParameters );
 529            if ( $resourceData and
 530                 $resourceData['root-node'] !== null )
 531                $root =& $resourceData['root-node'];
 532        }
 533        eZDebug::accumulatorStop( 'template_load' );
 534        if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
 535        {
 536            $savedLocale = setlocale( LC_CTYPE, null );
 537            setlocale( LC_CTYPE, $resourceData['locales'] );
 538        }
 539
 540        $text = "";
 541
 542        if ( $root !== null or
 543             $resourceData['compiled-template'] )
 544        {
 545            if ( $this->ShowDetails )
 546                eZDebug::addTimingPoint( "Process" );
 547            eZDebug::accumulatorStart( 'template_processing', 'template_total', 'Template processing' );
 548
 549            $templateCompilationUsed = false;
 550            if ( $resourceData['compiled-template'] )
 551            {
 552                $textElements = array();
 553                if ( $this->executeCompiledTemplate( $resourceData, $textElements, "", "", $extraParameters ) )
 554                {
 555                    $text = implode( '', $textElements );
 556                    $templateCompilationUsed = true;
 557                }
 558            }
 559            if ( !$templateCompilationUsed )
 560            {
 561                if ( eZTemplate::isDebugEnabled() )
 562                {
 563                    $fname = $resourceData['template-filename'];
 564                    eZDebug::writeDebug( "FETCH START URI: $template, $fname" );
 565                }
 566                $this->process( $root, $text, "", "" );
 567                if ( eZTemplate::isDebugEnabled() )
 568                    eZDebug::writeDebug( "FETCH END URI: $template, $fname" );
 569            }
 570
 571            eZDebug::accumulatorStop( 'template_processing' );
 572            if ( $this->ShowDetails )
 573                eZDebug::addTimingPoint( "Process done" );
 574        }
 575
 576        eZDebug::accumulatorStop( 'template_total' );
 577
 578        if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
 579        {
 580            setlocale( LC_CTYPE, $savedLocale );
 581        }
 582
 583        if ( $returnResourceData )
 584        {
 585            $resourceData['result_text'] = $text;
 586            return $resourceData;
 587        }
 588        return $text;
 589    }
 590
 591    function process( $root, &$text, $rootNamespace, $currentNamespace )
 592    {
 593        $this->createLocalVariablesList();
 594
 595        $textElements = array();
 596        $this->processNode( $root, $textElements, $rootNamespace, $currentNamespace );
 597        if ( is_array( $textElements ) )
 598            $text = implode( '', $textElements );
 599        else
 600            $text = $textElements;
 601
 602        $this->unsetLocalVariables();
 603        $this->destroyLocalVariablesList();
 604    }
 605
 606    function processNode( $node, &$textElements, $rootNamespace, $currentNamespace )
 607    {
 608        $rslt = null;
 609        $nodeType = $node[0];
 610        if ( $nodeType == eZTemplate::NODE_ROOT )
 611        {
 612            $children = $node[1];
 613            if ( $children )
 614            {
 615                foreach ( $children as $child )
 616                {
 617                    $this->processNode( $child, $textElements, $rootNamespace, $currentNamespace );
 618                    if ( !is_array( $textElements ) )
 619                        eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::root' );
 620                }
 621            }
 622        }
 623        else if ( $nodeType == eZTemplate::NODE_TEXT )
 624        {
 625            $textElements[] = $node[2];
 626        }
 627        else if ( $nodeType == eZTemplate::NODE_VARIABLE )
 628        {
 629            $variableData = $node[2];
 630            $variablePlacement = $node[3];
 631            $rslt = $this->processVariable( $textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace );
 632            if ( !is_array( $textElements ) )
 633                eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . '::variable' );
 634        }
 635        else if ( $nodeType == eZTemplate::NODE_FUNCTION )
 636        {
 637            $functionChildren = $node[1];
 638            $functionName = $node[2];
 639            $functionParameters = $node[3];
 640            $functionPlacement = $node[4];
 641            $rslt = $this->processFunction( $functionName, $textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace );
 642            if ( !is_array( $textElements ) )
 643                eZDebug::writeError( "Textelements is no longer array: '$textElements'", __METHOD__ . "::function( '$functionName' )" );
 644        }
 645
 646        return $rslt;
 647    }
 648
 649    function processVariable( &$textElements, $variableData, $variablePlacement, $rootNamespace, $currentNamespace )
 650    {
 651        $value = $this->elementValue( $variableData, $rootNamespace, $currentNamespace, $variablePlacement );
 652        $this->appendElementText( $textElements, $value, $rootNamespace, $currentNamespace );
 653    }
 654
 655    function processFunction( $functionName, &$textElements, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace )
 656    {
 657        // Note: This code piece is replicated in the eZTemplateCompiler,
 658        //       if this code is changed the replicated code must be updated as well.
 659        $func = $this->Functions[$functionName];
 660        if ( is_array( $func ) )
 661        {
 662            $this->loadAndRegisterFunctions( $this->Functions[$functionName] );
 663            $func = $this->Functions[$functionName];
 664        }
 665        if ( isset( $func ) and
 666             is_object( $func ) )
 667        {
 668            if ( eZTemplate::isMethodDebugEnabled() )
 669                eZDebug::writeDebug( "START FUNCTION: $functionName" );
 670            $value = $func->process( $this, $textElements, $functionName, $functionChildren, $functionParameters, $functionPlacement, $rootNamespace, $currentNamespace );
 671            if ( eZTemplate::isMethodDebugEnabled() )
 672                eZDebug::writeDebug( "END FUNCTION: $functionName" );
 673            return $value;
 674        }
 675        else
 676        {
 677            $this->warning( "", "Function \"$functionName\" is not registered" );
 678        }
 679    }
 680
 681    function fetchFunctionObject( $functionName )
 682    {
 683        $func = $this->Functions[$functionName];
 684        if ( is_array( $func ) )
 685        {
 686            $this->loadAndRegisterFunctions( $this->Functions[$functionName] );
 687            $func = $this->Functions[$functionName];
 688        }
 689        return $func;
 690    }
 691
 692    /*!
 693     Loads the template using the URI $uri and parses it.
 694     \return The root node of the tree if \a $returnResourceData is false,
 695             if \c true the entire resource data structure.
 696    */
 697    function load( $uri, $extraParameters = false, $returnResourceData = false )
 698    {
 699        $resourceData = $this->loadURIRoot( $uri, true, $extraParameters );
 700        if ( !$resourceData or
 701             $resourceData['root-node'] === null )
 702        {
 703            $retValue = null;
 704            return $retValue;
 705        }
 706        else
 707            return $resourceData['root-node'];
 708    }
 709
 710    function parse( $sourceText, &$rootElement, $rootNamespace, &$resourceData )
 711    {
 712        $parser = eZTemplateMultiPassParser::instance();
 713        $parser->parse( $this, $sourceText, $rootElement, $rootNamespace, $resourceData );
 714    }
 715
 716    function loadURIData( $resourceObject, $uri, $resourceName, $template, &$extraParameters, $displayErrors = true )
 717    {
 718        $resourceData = $this->resourceData( $resourceObject, $uri, $resourceName, $template );
 719
 720        $resourceData['text'] = null;
 721        $resourceData['root-node'] = null;
 722        $resourceData['compiled-template'] = false;
 723        $resourceData['time-stamp'] = null;
 724        $resourceData['key-data'] = null;
 725        $resourceData['locales'] = null;
 726
 727        if ( !$resourceObject->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters ) )
 728        {
 729            $resourceData = null;
 730            if ( $displayErrors )
 731                $this->warning( "", "No template could be loaded for \"$template\" using resource \"$resourceName\"" );
 732        }
 733        return $resourceData;
 734    }
 735
 736    /*!
 737     \static
 738     Creates a resource data structure of the parameters and returns it.
 739     This structure is passed to various parts of the template system.
 740
 741     \note If you only have the URI you should call resourceFor() first to
 742           figure out the resource handler.
 743    */
 744    function resourceData( $resourceObject, $uri, $resourceName, $templateName )
 745    {
 746        $resourceData = array();
 747        $resourceData['uri'] = $uri;
 748        $resourceData['resource'] = $resourceName;
 749        $resourceData['template-name'] = $templateName;
 750        $resourceData['template-filename'] = $templateName;
 751        $resourceData['handler'] = $resourceObject;
 752        $resourceData['test-compile'] = $this->TestCompile;
 753        return $resourceData;
 754    }
 755
 756    /*!
 757     Loads the template using the URI $uri and returns a structure with the text and timestamp,
 758     false otherwise.
 759     The structure keys are:
 760     - "text", the text.
 761     - "time-stamp", the timestamp.
 762    */
 763    function loadURIRoot( $uri, $displayErrors = true, &$extraParameters )
 764    {
 765        $res = "";
 766        $template = "";
 767        $resobj = $this->resourceFor( $uri, $res, $template );
 768
 769        if ( !is_object( $resobj ) )
 770        {
 771            if ( $displayErrors )
 772                $this->warning( "", "No resource handler for \"$res\" and no default resource handler, aborting." );
 773            return null;
 774        }
 775
 776        $canCache = true;
 777        if ( !$resobj->servesStaticData() )
 778            $canCache = false;
 779        if ( !$this->isCachingAllowed() )
 780            $canCache = false;
 781
 782        $resourceData = $this->loadURIData( $resobj, $uri, $res, $template, $extraParameters, $displayErrors );
 783
 784        if ( $resourceData )
 785        {
 786            eZTemplate::appendTemplateToStatisticsIfNeeded( $resourceData['template-name'], $resourceData['template-filename'] );
 787            $this->appendTemplateFetch( $resourceData['template-filename'] );
 788
 789            if ( !$resourceData['compiled-template'] and
 790                 $resourceData['root-node'] === null )
 791            {
 792                $resourceData['root-node'] = array( eZTemplate::NODE_ROOT, false );
 793                $templateText = $resourceData["text"];
 794                $keyData = $resourceData['key-data'];
 795                $this->setIncludeText( $uri, $templateText );
 796                $rootNamespace = '';
 797                $this->parse( $templateText, $resourceData['root-node'], $rootNamespace, $resourceData );
 798
 799                if ( eZTemplate::isDebugEnabled() )
 800                {
 801                    $this->appendDebugNodes( $resourceData['root-node'], $resourceData );
 802                }
 803
 804                if ( $canCache )
 805                    $resobj->setCachedTemplateTree( $keyData, $uri, $res, $template, $extraParameters, $resourceData['root-node'] );
 806            }
 807            if ( !$resourceData['compiled-template'] and
 808                 $canCache and
 809                 $this->canCompileTemplate( $resourceData, $extraParameters ) )
 810            {
 811                $generateStatus = $this->compileTemplate( $resourceData, $extraParameters );
 812                if ( $generateStatus )
 813                    $resourceData['compiled-template'] = true;
 814            }
 815        }
 816
 817        return $resourceData;
 818    }
 819
 820    function processURI( $uri, $displayErrors = true, &$extraParameters,
 821                         &$textElements, $rootNamespace, $currentNamespace )
 822    {
 823        $this->Level++;
 824        if ( $this->Level > $this->MaxLevel )
 825        {
 826            eZDebug::writeError( $this->MaxLevelWarning, __METHOD__ . " Level: $this->Level @ $uri" );
 827            $textElements[] = $this->MaxLevelWarning;
 828            $this->Level--;
 829            return;
 830        }
 831        $resourceData = $this->loadURIRoot( $uri, $displayErrors, $extraParameters );
 832        if ( !$resourceData or
 833             ( !$resourceData['compiled-template'] and
 834               $resourceData['root-node'] === null ) )
 835        {
 836            $this->Level--;
 837            return;
 838        }
 839
 840        $templateCompilationUsed = false;
 841
 842        if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
 843        {
 844            $savedLocale = setlocale( LC_CTYPE, null );
 845            setlocale( LC_CTYPE, $resourceData['locales'] );
 846        }
 847
 848        if ( $resourceData['compiled-template'] )
 849        {
 850            if ( $this->executeCompiledTemplate( $resourceData, $textElements, $rootNamespace, $currentNamespace, $extraParameters ) )
 851                $templateCompilationUsed = true;
 852        }
 853        if ( !$templateCompilationUsed )
 854        {
 855            $text = null;
 856            if ( eZTemplate::isDebugEnabled() )
 857            {
 858                $fname = $resourceData['template-filename'];
 859                eZDebug::writeDebug( "START URI: $uri, $fname" );
 860            }
 861            $this->process( $resourceData['root-node'], $text, $rootNamespace, $currentNamespace );
 862            if ( eZTemplate::isDebugEnabled() )
 863                eZDebug::writeDebug( "END URI: $uri, $fname" );
 864            $this->setIncludeOutput( $uri, $text );
 865            $textElements[] = $text;
 866        }
 867
 868        if ( $resourceData['locales'] && !empty( $resourceData['locales'] ) )
 869        {
 870            setlocale( LC_CTYPE, $savedLocale );
 871        }
 872        $this->Level--;
 873
 874    }
 875
 876    function canCompileTemplate( $resourceData, &$extraParameters )
 877    {
 878        $resourceObject = $resourceData['handler'];
 879        if ( !$resourceObject )
 880            return false;
 881        $canGenerate = $resourceObject->canCompileTemplate( $this, $resourceData, $extraParameters );
 882        return $canGenerate;
 883    }
 884
 885    /*!
 886     Validates the template file \a $file and returns \c true if the file has correct syntax.
 887     \param $returnResourceData If \c true then the returned value will be the resourcedata structure
 888     \sa compileTemplateFile(), fetch()
 889    */
 890    function validateTemplateFile( $file, $returnResourceData = false )
 891    {
 892        $this->resetErrorLog();
 893
 894        if ( !file_exists( $file ) )
 895            return false;
 896        $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName );
 897        if ( !$resourceHandler )
 898            return false;
 899        $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName );
 900        $resourceData['key-data'] = "file:" . $file;
 901        $extraParameters = array();
 902
 903        // Disable caching/compiling while fetchin the resource
 904        // It will be restored afterwards
 905        $isCachingAllowed = $this->IsCachingAllowed;
 906        $this->IsCachingAllowed = false;
 907
 908        $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters );
 909
 910        // Restore previous caching flag
 911        $this->IsCachingAllowed = $isCachingAllowed;
 912
 913        $root =& $resourceData['root-node'];
 914        $root = array( eZTemplate::NODE_ROOT, false );
 915        $templateText = $resourceData["text"];
 916        $rootNamespace = '';
 917        $this->parse( $templateText, $root, $rootNamespace, $resourceData );
 918        if ( eZTemplate::isDebugEnabled() )
 919        {
 920            $this->appendDebugNodes( $root, $resourceData );
 921        }
 922
 923        $result = !$this->hasErrors() and !$this->hasWarnings();
 924        if ( $returnResourceData )
 925        {
 926            $resourceData['result'] = $result;
 927            return $resourceData;
 928        }
 929        return $result;
 930    }
 931
 932    /*!
 933     Compiles the template file \a $file and returns \c true if the compilation was OK.
 934     \param $returnResourceData If \c true then the returned value will be the resourcedata structure
 935     \sa validateTemplateFile(), fetch()
 936    */
 937    function compileTemplateFile( $file, $returnResourceData = false )
 938    {
 939        $this->resetErrorLog();
 940
 941        if ( !file_exists( $file ) )
 942            return false;
 943        $resourceHandler = $this->resourceFor( $file, $resourceName, $templateName );
 944        if ( !$resourceHandler )
 945            return false;
 946        $resourceData = $this->resourceData( $resourceHandler, $file, $resourceName, $templateName );
 947        $resourceData['key-data'] = "file:" . $file;
 948        $key = md5( $resourceData['key-data'] );
 949        $extraParameters = array();
 950        $resourceHandler->handleResource( $this, $resourceData, eZTemplate::RESOURCE_FETCH, $extraParameters );
 951
 952        $isCompiled = false;
 953        if ( isset( $resourceData['compiled-template'] ) )
 954            $isCompiled = $resourceData['compiled-template'];
 955
 956        if ( !$isCompiled )
 957        {
 958            $root =& $resourceData['root-node'];
 959            $root = array( eZTemplate::NODE_ROOT, false );
 960            $templateText = $resourceData["text"];
 961            $rootNamespace = '';
 962            $this->parse( $templateText, $root, $rootNamespace, $resourceData );
 963            if ( eZTemplate::isDebugEnabled() )
 964            {
 965                $this->appendDebugNodes( $root, $resourceData );
 966            }
 967
 968            $result = eZTemplateCompiler::compileTemplate( $this, $key, $resourceData );
 969        }
 970        else
 971        {
 972            $result = true;
 973        }
 974
 975        if ( $returnResourceData )
 976        {
 977            $resourceData['result'] = $result;
 978            return $resourceData;
 979        }
 980        return $result;
 981    }
 982
 983    function compileTemplate( &$resourceData, &$extraParameters )
 984    {
 985        $resourceObject = $resourceData['handler'];
 986        if ( !$resourceObject )
 987            return false;
 988        $keyData = $resourceData['key-data'];
 989        $uri = $resourceData['uri'];
 990        $resourceName = $resourceData['resource'];
 991        $templatePath = $resourceData['template-name'];
 992        return $resourceObject->compileTemplate( $this, $keyData, $uri, $resourceName, $templatePath, $extraParameters, $resourceData );
 993    }
 994
 995    function executeCompiledTemplate( &$resourceData, &$textElements, $rootNamespace, $currentNamespace, &$extraParameters )
 996    {
 997        $resourceObject = $resourceData['handler'];
 998        if ( !$resourceObject )
 999            return false;
1000        $keyData = $resourceData['key-data'];
1001        $uri = $resourceData['uri'];
1002        $resourceName = $resourceData['resource'];
1003        $templatePath = $resourceData['template-name'];
1004        $timestamp = $resourceData['time-stamp'];
1005        return $resourceObject->executeCompiledTemplate( $this, $textElements,
1006                                                         $keyData, $uri, $resourceData, $templatePath,
1007                                                         $extraParameters, $timestamp,
1008                                                         $rootNamespace, $currentNamespace );
1009    }
1010
1011    /*!
1012     Returns the resource object for URI $uri. If a resource type is specified
1013     in the URI it is extracted and set in $res. The template name is set in $template
1014     without any resource specifier. To specify a resource the name and a ":" is
1015     prepended to the URI, for instance file:my.tpl.
1016     If no resource type is found the URI the default resource handler is used.
1017    */
1018    function resourceFor( $uri, &$res, &$template )
1019    {
1020        $args = explode( ":", $uri );
1021        if ( isset( $args[1] ) )
1022        {
1023            $res = $args[0];
1024            $template = $args[1];
1025        }
1026        else
1027            $template = $uri;
1028        if ( eZTemplate::isDebugEnabled() )
1029        {
1030            eZDebug::writeNotice( "eZTemplate: Loading template \"$template\" with resource \"$res\"" );
1031        }
1032        if ( isset( $this->Resources[$res] ) and is_object( $this->Resources[$res] ) )
1033        {
1034            return $this->Resources[$res];
1035        }
1036        return $this->DefaultResource;
1037    }
1038
1039    /*!
1040     \return The resource handler object for resource name \a $resourceName.
1041     \sa resourceFor
1042    */
1043    function resourceHandler( $resourceName )
1044    {
1045        if ( isset( $this->Resources[$resourceName] ) &&
1046             is_object( $this->Resources[$resourceName] ) )
1047        {
1048            return $this->Resources[$resourceName];
1049        }
1050        return $this->DefaultResource;
1051    }
1052
1053    function hasChildren( &$function, $functionName )
1054    {
1055        $hasChildren = $function->hasChildren();
1056        if ( is_array( $hasChildren ) )
1057            return $hasChildren[$functionName];
1058        else
1059            return $hasChildren;
1060     }
1061
1062    /*!
1063     Returns the empty variable type.
1064    */
1065    function emptyVariable()
1066    {
1067        return array( "type" => "null" );
1068    }
1069
1070    /*!
1071     \static
1072    */
1073    function mergeNamespace( $rootNamespace, $additionalNamespace )
1074    {
1075        $namespace = $rootNamespace;
1076        if ( $namespace == '' )
1077            $namespace = $additionalNamespace;
1078        else if ( $additionalNamespace != '' )
1079            $namespace = "$namespace:$additionalNamespace";
1080        return $namespace;
1081    }
1082
1083    /*!
1084     Returns the actual value of a template type or null if an unknown type.
1085    */
1086    function elementValue( &$dataElements, $rootNamespace, $currentNamespace, $placement = false,
1087                           $checkExistance = false, $checkForProxy = false )
1088    {
1089        /*
1090         * We use a small dirty hack in this function...
1091         * To help the caller to determine if the value was a proxy object,
1092         * we store boolean true to $dataElements['proxy-object-found'] in this case.
1093         * (it's up to caller to remove this garbage from $dataElements...)
1094         * This behaviour is enabled by $checkForProxy parameter.
1095         */
1096
1097        $value = null;
1098        if ( !is_array( $dataElements ) )
1099        {
1100            $this->error( "elementValue",
1101                          "Missing array data structure, got " . gettype( $dataElements ) );
1102            return null;
1103        }
1104        foreach ( $dataElements as $dataElement )
1105        {
1106            if ( $dataElement === null )
1107            {
1108                return null;
1109            }
1110            $dataType = $dataElement[0];
1111            switch ( $dataType )
1112            {
1113                case eZTemplate::TYPE_VOID:
1114                {
1115                    if ( !$checkExistance )
1116                        $this->warning( 'elementValue',
1117                                        'Found void datatype, should not be used' );
1118                    else
1119                    {
1120                        return null;
1121                    }
1122                } break;
1123                case eZTemplate::TYPE_STRING:
1124                case eZTemplate::TYPE_NUMERIC:
1125                case eZTemplate::TYPE_IDENTIFIER:
1126                case eZTemplate::TYPE_BOOLEAN:
1127                case eZTemplate::TYPE_ARRAY:
1128                {
1129                    $value = $dataElement[1];
1130                } break;
1131                case eZTemplate::TYPE_VARIABLE:
1132                {
1133                    $variableData = $dataElement[1];
1134                    $variableNamespace = $variableData[0];
1135                    $variableNamespaceScope = $variableData[1];
1136                    $variableName = $variableData[2];
1137                    if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_GLOBAL )
1138                        $namespace = $variableNamespace;
1139                    else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_LOCAL )
1140                        $namespace = $this->mergeNamespace( $rootNamespace, $variableNamespace );
1141                    else if ( $variableNamespaceScope == eZTemplate::NAMESPACE_SCOPE_RELATIVE )
1142                        $namespace = $this->mergeNamespace( $currentNamespace, $variableNamespace );
1143                    else
1144                        $namespace = false;
1145                    if ( $this->hasVariable( $variableName, $namespace ) )
1146                    {
1147                        $value = $this->variable( $variableName, $namespace );
1148                    }
1149                    else
1150                    {
1151                        if ( !$checkExistance )
1152                            $this->error( '', "Unknown template variable '$variableName' in namespace '$namespace'", $placement );
1153                        {
1154                            return null;
1155                        }
1156                    }
1157                } break;
1158                case eZTemplate::TYPE_ATTRIBUTE:
1159                {
1160                    $attributeData = $dataElement[1];
1161                    $attributeValue = $this->elementValue( $attributeData, $rootNamespace, $currentNamespace, false, $checkExistance );
1162
1163                    if ( $attributeValue !== null )
1164                    {
1165                        if ( !is_numeric( $attributeValue ) and
1166                             !is_string( $attributeValue ) and
1167                             !is_bool( $attributeValue ) )
1168                        {
1169                            if ( !$checkExistance )
1170                                $this->error( "",
1171                                              "Cannot use type " . gettype( $attributeValue ) . " for attribute lookup", $placement );
1172                            {
1173                                return null;
1174                            }
1175                        }
1176                        if ( is_array( $value ) )
1177                        {
1178                            if ( array_key_exists( $attributeValue, $value ) )
1179                            {
1180                                $value = $value[$attributeValue];
1181                            }
1182                            else
1183                            {
1184                                if ( !$checkExistance )
1185                                {
1186                                    $arrayAttributeList = array_keys( $value );
1187                                    $arrayCount = count( $arrayAttributeList );
1188                                    $errorMessage = "No such attribute for array($arrayCount): $attributeValue";
1189                                    $chooseText = "Choose one of following: ";
1190                                    $errorMessage .= "\n$chooseText";
1191                                    $errorMessage .= $this->expandAttributes( $arrayAttributeList, $chooseText, 25 );
1192                                    $this->error( "",
1193                                                  $errorMessage, $placement );
1194                                }
1195                                return null;
1196                            }
1197                        }
1198                        else if ( is_object( $value ) )
1199                        {
1200                            if ( method_exists( $value, "attribute" ) and
1201                                 method_exists( $value, "hasAttribute" ) )
1202                            {
1203                                if ( $value->hasAttribute( $attributeValue ) )
1204                                {
1205                                    $value = $value->attribute( $attributeValue );
1206                                }
1207                                else
1208                                {
1209                                    if ( !$checkExistance )
1210                                    {
1211                                        $objectAttributeList = array();
1212                                        if ( method_exists( $value, 'attributes' ) )
1213                                            $objectAttributeList = $value->attributes();
1214                                        $objectClass= get_class( $value );
1215                                        $errorMessage = "No such attribute for object($objectClass): $attributeValue";
1216                                        $chooseText = "Choose one of following: ";
1217                                        $errorMessage .= "\n$chooseText";
1218                                        $errorMessage .= $this->expandAttributes( $objectAttributeList, $chooseText, 25 );
1219                                        $this->error( "",
1220                                                      $errorMessage, $placement );
1221                                    }
1222                                    return null;
1223                                }
1224                            }
1225                            else
1226                            {
1227                                if ( !$checkExistance )
1228                                    $this->error( "",
1229                                                  "Cannot retrieve attribute of object(" . get_class( $value ) .
1230                                                  "), no attribute functions available",
1231                                                  $placement );
1232                                return null;
1233                            }
1234                        }
1235                        else
1236                        {
1237                            if ( !$checkExistance )
1238                                $this->error( "",
1239                                              "Cannot retrieve attribute of a " . gettype( $value ),
1240                                              $placement );
1241                            return null;
1242                        }
1243                    }
1244                    else
1245                    {
1246                        if ( !$checkExistance )
1247                            $this->error( '',
1248                                          'Attribute value was null, cannot get attribute',
1249                                          $placement );
1250                        return null;
1251                    }
1252                } break;
1253                case eZTemplate::TYPE_OPERATOR:
1254                {
1255                    $operatorParameters = $dataElement[1];
1256                    $operatorName = $operatorParameters[0];
1257                    $operatorParameters = array_splice( $operatorParameters, 1 );
1258                    if ( is_object( $value ) and
1259                         method_exists( $value, 'templateValue' ) )
1260                    {
1261                        if ( $checkForProxy )
1262                            $dataElements['proxy-object-found'] = true;
1263                        $value = $value->templateValue();
1264                    }
1265                    $valueData = array( 'value' => $value );
1266                    $this->processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace,
1267                                            $valueData, $placement, $checkExistance );
1268                    $value = $valueData['value'];
1269                } break;
1270                default:
1271                {
1272                    if ( !$checkExistance )
1273                        $this->error( "elementValue",
1274                                      "Unknown data type: '$dataType'" );
1275                    return null;
1276                }
1277            }
1278        }
1279        if ( is_object( $value ) and
1280             method_exists( $value, 'templateValue' ) )
1281        {
1282            if ( $checkForProxy )
1283                $dataElements['proxy-object-found'] = true;
1284            return $value->templateValue();
1285        }
1286        return $value;
1287    }
1288
1289    function expandAttributes( $attributeList, $chooseText, $maxThreshold, $minThreshold = 1 )
1290    {
1291        $errorMessage = '';
1292        $attributeCount = count( $attributeList );
1293        if ( $attributeCount < $minThreshold )
1294            return $errorMessage;
1295        if ( $attributeCount < $maxThreshold )
1296        {
1297            $chooseLength = strlen( $chooseText );
1298            $attributeText = '';
1299            $i = 0;
1300            foreach ( $attributeList as $attributeName )
1301            {
1302                if ( $i > 0 )
1303                    $attributeText .= ",";
1304                if ( strlen( $attributeText ) > 40 )
1305                {
1306                    $attributeText .= "\n";
1307                    $errorMessage .= $attributeText;
1308                    $errorMessage .= str_repeat( ' ', $chooseLength );
1309                    $attributeText = '';
1310                }
1311                else if ( $i > 0 )
1312                    $attributeText .= " ";
1313                $attributeText .= $attributeName;
1314                ++$i;
1315            }
1316            $errorMessage .= $attributeText;
1317        }
1318        return $errorMessage;
1319    }
1320
1321    function processOperator( $operatorName, $operatorParameters, $rootNamespace, $currentNamespace,
1322                              &$valueData, $placement = false, $checkExistance = false )
1323    {
1324

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