|
eZ Publish
[4.0]
|
00001 <?php 00002 // 00003 // Definition of eZTemplateElementParser class 00004 // 00005 // Created on: <27-Nov-2002 10:53:36 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 eztemplateelementparser.php 00032 */ 00033 00034 /*! 00035 \class eZTemplateElementParser eztemplateelementparser.php 00036 \brief The class eZTemplateElementParser does 00037 00038 */ 00039 00040 //include_once( 'lib/eztemplate/classes/eztemplate.php' ); 00041 00042 class eZTemplateElementParser 00043 { 00044 /*! 00045 Constructor 00046 */ 00047 function eZTemplateElementParser() 00048 { 00049 } 00050 00051 function templateTypeName( $type ) 00052 { 00053 switch ( $type ) 00054 { 00055 case eZTemplate::TYPE_STRING: 00056 return "string"; 00057 case eZTemplate::TYPE_NUMERIC: 00058 return "numeric"; 00059 case eZTemplate::TYPE_IDENTIFIER: 00060 return "identifier"; 00061 case eZTemplate::TYPE_VARIABLE: 00062 return "variable"; 00063 case eZTemplate::TYPE_ATTRIBUTE: 00064 return "attribute"; 00065 } 00066 return null; 00067 } 00068 00069 /*! 00070 Parses the variable and operators into a structure. 00071 */ 00072 function parseVariableTag( $tpl, $relatedTemplateName, &$text, $startPosition, &$endPosition, $textLength, $defaultNamespace, 00073 $allowedType = false, $maxElements = false, $endMarker = false, 00074 $undefinedType = eZTemplate::TYPE_ATTRIBUTE ) 00075 { 00076 $currentPosition = $startPosition; 00077 $elements = array(); 00078 $lastPosition = false; 00079 if ( $allowedType === false ) 00080 $allowedType = eZTemplate::TYPE_BASIC; 00081 while ( $currentPosition < $textLength and 00082 ( $maxElements === false or 00083 count( $elements ) < $maxElements ) ) 00084 { 00085 if ( $lastPosition !== false and 00086 $lastPosition == $currentPosition ) 00087 { 00088 $tpl->error( "ElementParser::parseVariableTag", "parser error @ $relatedTemplateName[$currentPosition]\n" . 00089 "Parser position did not move, this is most likely a bug in the template parser." ); 00090 break; 00091 } 00092 $lastPosition = $currentPosition; 00093 $currentPosition = $this->whitespaceEndPos( $tpl, $text, $currentPosition, $textLength ); 00094 if ( $currentPosition >= $textLength ) 00095 continue; 00096 if ( $endMarker !== false ) 00097 { 00098 if ( $currentPosition < $textLength and 00099 strpos( $endMarker, $text[$currentPosition] ) !== false ) 00100 break; 00101 } 00102 if ( $text[$currentPosition] == '|' ) 00103 { 00104 if ( !( $allowedType & eZTemplate::TYPE_OPERATOR_BIT ) ) 00105 { 00106 $currentPosition = $lastPosition; 00107 break; 00108 } 00109 $maxOperatorElements = 1; 00110 $operatorEndMarker = false; 00111 $currentOperatorPosition = $currentPosition + 1; 00112 $operatorEndPosition = false; 00113 $operatorElements = $this->parseVariableTag( $tpl, $relatedTemplateName, $text, $currentOperatorPosition, $operatorEndPosition, $textLength, $defaultNamespace, 00114 eZTemplate::TYPE_OPERATOR_BIT, $maxOperatorElements, $operatorEndMarker, eZTemplate::TYPE_OPERATOR ); 00115 if ( $operatorEndPosition > $currentOperatorPosition ) 00116 { 00117 $elements = array_merge( $elements, $operatorElements ); 00118 $currentPosition = $operatorEndPosition; 00119 } 00120 } 00121 else if ( $text[$currentPosition] == '.' or 00122 $text[$currentPosition] == '[' ) 00123 { 00124 if ( !( $allowedType & eZTemplate::TYPE_ATTRIBUTE_BIT ) ) 00125 { 00126 $currentPosition = $lastPosition; 00127 break; 00128 } 00129 $maxAttributeElements = 1; 00130 $attributeEndMarker = false; 00131 if ( $text[$currentPosition] == '[' ) 00132 { 00133 $maxAttributeElements = false; 00134 $attributeEndMarker = ']'; 00135 } 00136 ++$currentPosition; 00137 $attributeEndPosition = false; 00138 $attributeElements = $this->parseVariableTag( $tpl, $relatedTemplateName, $text, $currentPosition, $attributeEndPosition, $textLength, $defaultNamespace, 00139 eZTemplate::TYPE_BASIC, $maxAttributeElements, $attributeEndMarker ); 00140 if ( $attributeEndPosition > $currentPosition ) 00141 { 00142 $element = array( eZTemplate::TYPE_ATTRIBUTE, // type 00143 $attributeElements, // content 00144 false // debug 00145 ); 00146 $elements[] = $element; 00147 if ( $attributeEndMarker !== false ) 00148 $attributeEndPosition += strlen( $attributeEndMarker ); 00149 $currentPosition = $attributeEndPosition; 00150 } 00151 } 00152 else if ( $text[$currentPosition] == "$" ) 00153 { 00154 if ( !( $allowedType & eZTemplate::TYPE_VARIABLE_BIT ) ) 00155 { 00156 $currentPosition = $lastPosition; 00157 break; 00158 } 00159 ++$currentPosition; 00160 $variableEndPosition = $this->variableEndPos( $tpl, $relatedTemplateName, $text, $currentPosition, $textLength, 00161 $variableNamespace, $variableName, $namespaceScope ); 00162 if ( $variableEndPosition > $currentPosition ) 00163 { 00164 $element = array( eZTemplate::TYPE_VARIABLE, // type 00165 array( $variableNamespace, 00166 $namespaceScope, 00167 $variableName ), // content 00168 false // debug 00169 ); 00170 $elements[] = $element; 00171 $currentPosition = $variableEndPosition; 00172 $allowedType = eZTemplate::TYPE_MODIFIER_MASK; 00173 } 00174 } 00175 else if ( $text[$currentPosition] == "'" or 00176 $text[$currentPosition] == '"' ) 00177 { 00178 if ( !( $allowedType & eZTemplate::TYPE_STRING_BIT) ) 00179 { 00180 $currentPosition = $lastPosition; 00181 break; 00182 } 00183 $quote = $text[$currentPosition]; 00184 ++$currentPosition; 00185 $quoteEndPosition = $this->quoteEndPos( $tpl, $text, $currentPosition, $textLength, $quote ); 00186 $string = substr( $text, $currentPosition, $quoteEndPosition - $currentPosition ); 00187 $string = $this->unescapeCharacters( $string ); 00188 $element = array( eZTemplate::TYPE_STRING, // type 00189 $string, // content 00190 false // debug 00191 ); 00192 $elements[] = $element; 00193 $currentPosition = $quoteEndPosition + 1; 00194 $allowedType = eZTemplate::TYPE_OPERATOR_BIT; 00195 } 00196 else 00197 { 00198 $float = true; 00199 $numericEndPosition = $this->numericEndPos( $tpl, $text, $currentPosition, $textLength, $float ); 00200 if ( $numericEndPosition > $currentPosition ) 00201 { 00202 if ( !( $allowedType & eZTemplate::TYPE_NUMERIC_BIT ) ) 00203 { 00204 $currentPosition = $lastPosition; 00205 break; 00206 } 00207 // We got a number 00208 $number = substr( $text, $currentPosition, $numericEndPosition - $currentPosition ); 00209 if ( $float ) 00210 $number = (float)$number; 00211 else 00212 $number = (int)$number; 00213 $element = array( eZTemplate::TYPE_NUMERIC, // type 00214 $number, // content 00215 false // debug 00216 ); 00217 $elements[] = $element; 00218 $currentPosition = $numericEndPosition; 00219 $allowedType = eZTemplate::TYPE_OPERATOR_BIT; 00220 } 00221 else 00222 { 00223 $identifierEndPosition = $this->identifierEndPosition( $tpl, $text, $currentPosition, $textLength ); 00224 if ( $currentPosition == $identifierEndPosition ) 00225 { 00226 $currentPosition = $lastPosition; 00227 break; 00228 } 00229 if ( ( $identifierEndPosition < $textLength and 00230 $text[$identifierEndPosition] == '(' ) or 00231 $undefinedType == eZTemplate::TYPE_OPERATOR ) 00232 { 00233 if ( !( $allowedType & eZTemplate::TYPE_OPERATOR_BIT ) ) 00234 { 00235 $currentPosition = $lastPosition; 00236 break; 00237 } 00238 $operatorName = substr( $text, $currentPosition, $identifierEndPosition - $currentPosition ); 00239 $operatorParameterElements = array( $operatorName ); 00240 00241 if ( $identifierEndPosition < $textLength and 00242 $text[$identifierEndPosition] == '(' ) 00243 { 00244 $currentPosition = $identifierEndPosition + 1; 00245 $currentOperatorPosition = $currentPosition; 00246 $operatorDone = false; 00247 $parameterCount = 0; 00248 while ( !$operatorDone ) 00249 { 00250 $operatorEndPosition = false; 00251 $operatorParameterElement = $this->parseVariableTag( $tpl, $relatedTemplateName, $text, $currentOperatorPosition, $operatorEndPosition, $textLength, $defaultNamespace, 00252 eZTemplate::TYPE_BASIC, false, ',)' ); 00253 if ( $operatorEndPosition < $textLength and 00254 $text[$operatorEndPosition] == ',' ) 00255 { 00256 if ( $operatorEndPosition == $currentOperatorPosition ) 00257 { 00258 $operatorParameterElements[] = null; 00259 } 00260 else 00261 $operatorParameterElements[] = $operatorParameterElement; 00262 ++$parameterCount; 00263 $currentOperatorPosition = $operatorEndPosition + 1; 00264 } 00265 else if ( $operatorEndPosition < $textLength and 00266 $text[$operatorEndPosition] == ')' ) 00267 { 00268 $operatorDone = true; 00269 if ( $operatorEndPosition == $currentOperatorPosition ) 00270 { 00271 if ( $parameterCount > 0 ) 00272 { 00273 $operatorParameterElements[] = null; 00274 ++$parameterCount; 00275 } 00276 } 00277 else 00278 { 00279 $operatorParameterElements[] = $operatorParameterElement; 00280 ++$parameterCount; 00281 } 00282 ++$operatorEndPosition; 00283 } 00284 else 00285 { 00286 $currentPosition = $lastPosition; 00287 break; 00288 } 00289 } 00290 if ( !$operatorDone ) 00291 break; 00292 } 00293 else 00294 { 00295 $operatorEndPosition = $identifierEndPosition; 00296 } 00297 00298 $element = array( eZTemplate::TYPE_OPERATOR, // type 00299 $operatorParameterElements, // content 00300 false // debug 00301 ); 00302 $elements[] = $element; 00303 $currentPosition = $operatorEndPosition; 00304 $allowedType = eZTemplate::TYPE_MODIFIER_MASK; 00305 } 00306 else 00307 { 00308 if ( !( $allowedType & eZTemplate::TYPE_IDENTIFIER_BIT ) ) 00309 { 00310 $currentPosition = $lastPosition; 00311 break; 00312 } 00313 $identifier = substr( $text, $currentPosition, $identifierEndPosition - $currentPosition ); 00314 $element = array( eZTemplate::TYPE_IDENTIFIER, // type 00315 $identifier, // content 00316 false // debug 00317 ); 00318 $elements[] = $element; 00319 $currentPosition = $identifierEndPosition; 00320 $allowedType = eZTemplate::TYPE_NONE; 00321 } 00322 } 00323 } 00324 } 00325 $endPosition = $currentPosition; 00326 return $elements; 00327 } 00328 00329 /*! 00330 Returns the end position of the variable. 00331 */ 00332 function variableEndPos( $tpl, $relatedTemplateName, &$text, $startPosition, $textLength, 00333 &$namespace, &$name, &$scope ) 00334 { 00335 $currentPosition = $startPosition; 00336 $namespaces = array(); 00337 $variableName = false; 00338 $lastPosition = false; 00339 $scopeType = eZTemplate::NAMESPACE_SCOPE_LOCAL; 00340 $scopeRead = false; 00341 while ( $currentPosition < $textLength ) 00342 { 00343 if ( $lastPosition !== false and 00344 $lastPosition == $currentPosition ) 00345 { 00346 $tpl->error( "ElementParser::variableEndPos", "parser error @ $relatedTemplateName\[" . $currentPosition . "]\n" . 00347 "Parser position did not move, this is most likely a bug in the template parser." ); 00348 break; 00349 } 00350 $lastPosition = $currentPosition; 00351 if ( $text[$currentPosition] == '#' ) 00352 { 00353 if ( $scopeRead ) 00354 { 00355 $tpl->error( "ElementParser::variableEndPos", "parser error @ $relatedTemplateName\[" . $currentPosition . "]\n" . 00356 "Namespace scope already declared, cannot set to global." ); 00357 } 00358 else 00359 { 00360 $scopeType = eZTemplate::NAMESPACE_SCOPE_GLOBAL; 00361 } 00362 $scopeRead = true; 00363 ++$currentPosition; 00364 } 00365 else if ( $text[$currentPosition] == ':' ) 00366 { 00367 if ( $scopeRead ) 00368 { 00369 $tpl->error( "ElementParser::variableEndPos", "parser error @ $relatedTemplateName\[" . $currentPosition . "]\n" . 00370 "Namespace scope already declared, cannot set to relative." ); 00371 } 00372 else 00373 { 00374 $scopeType = eZTemplate::NAMESPACE_SCOPE_RELATIVE; 00375 } 00376 $scopeRead = true; 00377 ++$currentPosition; 00378 } 00379 else 00380 { 00381 $identifierEndPosition = $this->identifierEndPosition( $tpl, $text, $currentPosition, $textLength ); 00382 if ( $identifierEndPosition > $currentPosition ) 00383 { 00384 $identifier = substr( $text, $currentPosition, $identifierEndPosition - $currentPosition ); 00385 $currentPosition = $identifierEndPosition; 00386 if ( $identifierEndPosition < $textLength and 00387 $text[$identifierEndPosition] == ':' ) 00388 { 00389 $namespaces[] = $identifier; 00390 ++$currentPosition; 00391 } 00392 else 00393 $variableName = $identifier; 00394 } 00395 else if ( $identifierEndPosition < $textLength and 00396 ( $text[$identifierEndPosition] != ":" and 00397 $text[$identifierEndPosition] != "#" ) ) 00398 { 00399 if ( $variableName === false ) 00400 { 00401 $tpl->error( "ElementParser::variableEndPos", "parser error @ $relatedTemplateName\[" . $currentPosition . "]\n" . 00402 "No variable name found, this is most likely a bug in the template parser." ); 00403 return $startPosition; 00404 } 00405 break; 00406 } 00407 else 00408 { 00409 $tpl->error( "ElementParser::variableEndPos", "parser error @ $relatedTemplateName\[" . $currentPosition . "]\n" . 00410 "Missing identifier for variable name or namespace, this is most likely a bug in the template parser." ); 00411 return $startPosition; 00412 } 00413 } 00414 } 00415 $scope = $scopeType; 00416 $namespace = implode( ':', $namespaces ); 00417 $name = $variableName; 00418 return $currentPosition; 00419 } 00420 00421 /*! 00422 Finds any escaped characters and unescapes them if they are one of: 00423 - \n - A newline 00424 - \r - A carriage return 00425 - \t - A tab 00426 - \' - A single quote 00427 - \" - A double quote 00428 00429 If the escaped character is not known it keeps both characters (escape + character). 00430 \return The transformed string without escape characters. 00431 */ 00432 function unescapeCharacters( $string ) 00433 { 00434 $newString = ''; 00435 $len = strlen( $string ); 00436 00437 // Fix escaped characters (double-quote, single-quote, newline, carriage-return, tab) 00438 for ( $i = 0; $i < $len; ++$i ) 00439 { 00440 $c = $string[$i]; 00441 00442 // If we don't have an escape character we keep it as-is 00443 if ( $c != "\\" ) 00444 { 00445 $newString .= $c; 00446 continue; 00447 } 00448 00449 // If this is the last character we keep it as-is 00450 if ( $i + 1 >= $len ) 00451 { 00452 $newString .= $c; 00453 break; 00454 } 00455 00456 $c2 = $string[++$i]; 00457 switch ( $c2 ) 00458 { 00459 case 'n': 00460 { 00461 $newString .= "\n"; 00462 } break; 00463 00464 case 'r': 00465 { 00466 $newString .= "\r"; 00467 } break; 00468 00469 case 't': 00470 { 00471 $newString .= "\t"; 00472 } break; 00473 00474 case "'": 00475 case '"': 00476 case '\\': 00477 { 00478 $newString .= $c2; 00479 } break; 00480 00481 // If it is not known we keep the characters. 00482 default: 00483 { 00484 $newString .= $c . $c2; 00485 } 00486 } 00487 } 00488 return $newString; 00489 } 00490 00491 /*! 00492 Returns the end position of the identifier. 00493 If no identifier was found the end position is returned. 00494 */ 00495 function identifierEndPosition( $tpl, &$text, $start_pos, $len ) 00496 { 00497 $pos = $start_pos; 00498 while ( $pos < $len ) 00499 { 00500 if ( !preg_match( "/^[a-zA-Z0-9_-]$/", $text[$pos] ) ) 00501 { 00502 return $pos; 00503 } 00504 ++$pos; 00505 } 00506 return $pos; 00507 } 00508 00509 /*! 00510 Returns the end position of the quote $quote. 00511 If no quote was found the end position is returned. 00512 */ 00513 function quoteEndPos( $tpl, &$text, $startPosition, $textLength, $quote ) 00514 { 00515 $currentPosition = $startPosition; 00516 while ( $currentPosition < $textLength ) 00517 { 00518 if ( $text[$currentPosition] == "\\" ) 00519 ++$currentPosition; 00520 else if ( $text[$currentPosition] == $quote ) 00521 return $currentPosition; 00522 ++$currentPosition; 00523 } 00524 return $currentPosition; 00525 } 00526 00527 /*! 00528 Returns the end position of the numeric. 00529 If no numeric was found the end position is returned. 00530 */ 00531 function numericEndPos( $tpl, &$text, $start_pos, $len, 00532 &$float ) 00533 { 00534 $pos = $start_pos; 00535 $has_comma = false; 00536 $numberPos = $pos; 00537 if ( $pos < $len ) 00538 { 00539 if ( $text[$pos] == '-' ) 00540 { 00541 ++$pos; 00542 $numberPos = $pos; 00543 } 00544 } 00545 while ( $pos < $len ) 00546 { 00547 if ( $text[$pos] == "." and $float ) 00548 { 00549 if ( $has_comma ) 00550 { 00551 if ( !$has_comma and 00552 $float ) 00553 $float = false; 00554 return $pos; 00555 } 00556 $has_comma = $pos; 00557 } 00558 else if ( $text[$pos] < '0' or $text[$pos] > '9' ) 00559 { 00560 if ( !$has_comma and 00561 $float ) 00562 $float = false; 00563 if ( $pos < $len and 00564 $has_comma and 00565 $pos == $has_comma + 1 ) 00566 { 00567 return $start_pos; 00568 } 00569 if ( $pos == $numberPos ) 00570 { 00571 return $start_pos; 00572 } 00573 return $pos; 00574 } 00575 ++$pos; 00576 } 00577 if ( !$has_comma and 00578 $float ) 00579 $float = false; 00580 if ( $has_comma and 00581 $start_pos + 1 == $pos ) 00582 { 00583 return $start_pos; 00584 } 00585 return $pos; 00586 } 00587 00588 /*! 00589 Returns the position of the first non-whitespace characters. 00590 */ 00591 function whitespaceEndPos( $tpl, &$text, $currentPosition, $textLength ) 00592 { 00593 if ( $currentPosition >= $textLength ) 00594 return $currentPosition; 00595 while( $currentPosition < $textLength and 00596 preg_match( "/[ \t\r\n]/", $text[$currentPosition] ) ) 00597 { 00598 ++$currentPosition; 00599 } 00600 return $currentPosition; 00601 } 00602 00603 /*! 00604 Returns the position of the first non-whitespace characters. 00605 */ 00606 function isWhitespace( $tpl, &$text, $startPosition ) 00607 { 00608 return preg_match( "/[ \t\r\n]/", $text[$startPosition] ); 00609 } 00610 00611 static function instance() 00612 { 00613 if ( !isset( $GLOBALS['eZTemplateElementParserInstance'] ) || 00614 !( $GLOBALS['eZTemplateElementParserInstance'] instanceof eZTemplateElementParser ) ) 00615 { 00616 $GLOBALS['eZTemplateElementParserInstance'] = new eZTemplateElementParser(); 00617 } 00618 00619 return $GLOBALS['eZTemplateElementParserInstance']; 00620 } 00621 00622 } 00623 00624 ?>