/** * Parses the comment by looking for the end marker \n. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { $cutOff = false; if (!$cursor->atEnd()) { $cursor->advance(2); $matches = $cursor->pregMatchComplete("#^([^}\r\n]*)(?:(?:})|(\r|\r\n|\n))#"); if ($matches) { // reached end of comment $cutOff = false; if (isset($matches[2])) { $cursor->advance($matches[2][1] + strlen($matches[2][0])); // Do not include the newline itself in the comment. $cutOff = -1; } else { $cursor->advance($matches[1][1] + strlen($matches[1][0])); } } else { $cursor->gotoEnd(); } $commentBlock = new ezcTemplateEolCommentTstNode($this->parser->source, $this->startCursor, clone $cursor); if ($cutOff) { $commentBlock->commentText = substr($commentBlock->text(), 2, $cutOff); } else { $commentBlock->commentText = substr($commentBlock->text(), 2); } $this->appendElement($commentBlock); return true; } return false; }
/** * Parses the comment by looking for the end marker * + }. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { $cursor->advance(); if ($cursor->atEnd()) { return false; } $checkInlineComment = false; // Check for a slash after the asterix, this typically means a typo for an inline comment // Better give an error for this to warn the user. if ($cursor->current() == '/') { $checkInlineComment = true; } $endPosition = $cursor->findPosition('*}'); if ($endPosition === false) { return false; } // If we found an end for an inline comment we need to check if there // is an end for an inline comment if ($checkInlineComment) { $commentCursor = $cursor->cursorAt($cursor->position, $endPosition); $commentCursor->advance(); $inlineCommentPosition = $commentCursor->findPosition('*/'); // We found the end of the inline comment, this is most likely a user error if ($inlineCommentPosition !== false) { $cursor->gotoPosition($inlineCommentPosition); return false; } } // reached end of comment $cursor->gotoPosition($endPosition + 2); $commentBlock = new ezcTemplateDocCommentTstNode($this->parser->source, clone $this->startCursor, clone $cursor); $commentBlock->commentText = substr($commentBlock->text(), 2, -2); $this->appendElement($commentBlock); return true; }
/** * Parses the boolean types by looking for either 'true' or 'false'. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { if (!$cursor->atEnd()) { // @todo This should check that there is no alphabetical characters // after the true|false. $matches = $cursor->pregMatchComplete("#^(true|false)(?:\\W)#i"); if ($matches === false) { return false; } $name = $matches[1][0]; $lower = strtolower($name); if ($name !== $lower) { $this->findNonLowercase(); throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_BOOLEAN_NOT_LOWERCASE); } $cursor->advance(strlen($name)); $bool = new ezcTemplateLiteralTstNode($this->parser->source, $this->startCursor, $cursor); $bool->value = $name == 'true'; $this->value = $bool->value; $this->element = $bool; $this->appendElement($bool); return true; } return false; }
/** * Parses the statements, foreach, while, if, elseif, etc. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { // Check if any control structure names are used. // Note: The code inside the (?:) brace ensures that the next character // is not an alphabetical character ie. a word boundary $matches = $cursor->pregMatchComplete("#^(tr|tr_context|foreach|while|if|elseif|else|switch|case|default|include|return|break|continue|skip|delimiter|increment|decrement|reset|charset|capture)(?:[^a-zA-Z0-9_])#"); if ($matches === false) { return false; } $name = $matches[1][0]; $cursor->advance(strlen($matches[1][0])); // control structure map $csMap = array(); $csMap['foreach'] = 'ForeachLoop'; $csMap['for'] = 'ForLoop'; $csMap['while'] = 'WhileLoop'; $csMap['if'] = 'IfCondition'; $csMap['elseif'] = 'IfCondition'; $csMap['else'] = 'IfCondition'; $csMap['switch'] = 'SwitchCondition'; $csMap['case'] = 'SwitchCondition'; $csMap['default'] = 'SwitchCondition'; $csMap['include'] = 'Include'; $csMap['return'] = 'Include'; $csMap['break'] = 'Loop'; $csMap['continue'] = 'Loop'; $csMap['skip'] = 'Delimiter'; $csMap['delimiter'] = 'Delimiter'; $csMap['increment'] = 'Cycle'; $csMap['decrement'] = 'Cycle'; $csMap['reset'] = 'Cycle'; $csMap['charset'] = 'Charset'; $csMap['capture'] = 'Capture'; $csMap['tr'] = 'Translation'; $csMap['tr_context'] = 'TranslationContext'; // tmp if (!isset($csMap[$name])) { return false; } $parser = 'ezcTemplate' . $csMap[$name] . 'SourceToTstParser'; // tmp if (!ezcBaseFeatures::classExists($parser)) { return false; } if (!ezcBaseFeatures::classExists($parser)) { throw new ezcTemplateInternalException("Requested parser class <{$parser}> does not exist"); } $controlStructureParser = new $parser($this->parser, $this, null); $this->block->name = $name; $controlStructureParser->block = $this->block; if (!$this->parseRequiredType($controlStructureParser)) { return false; } return true; }
/** * Parses the string types by looking for single or double quotes to start * the string. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { if (!$cursor->atEnd()) { $char = $cursor->current(); if ($char == '"' || $char == "'") { $string = new ezcTemplateLiteralTstNode($this->parser->source, $this->startCursor, $cursor); $string->quoteType = $char == "'" ? ezcTemplateLiteralTstNode::SINGLE_QUOTE : ezcTemplateLiteralTstNode::DOUBLE_QUOTE; $cursor->advance(); $nextChar = $cursor->current(); if ($nextChar === $char) { // We know it is an empty string, no need to extract $str = ""; $string->value = $str; $this->value = $string->value; $this->element = $string; $this->appendElement($string); $cursor->advance(); return true; } else { // Match: // ([^{$char}\\\\]|\A) : Matches non quote ('"', "'"), non backslash (\), or does match the begin of the statement. // (\\\\(\\\\|{$char}))* : Eat double slashes \\ and slash quotes: \' or \". $matches = $cursor->pregMatchComplete("#(?:([^{$char}\\\\]|\\A)(\\\\(\\\\|{$char}))*){$char}#"); if ($matches === false) { return false; } $cursor->advance($matches[0][1] + strlen($matches[0][0])); $str = (string) $this->startCursor->subString($cursor->position); $str = substr($str, 1, -1); $string->value = $str; $this->value = $string->value; $this->element = $string; $this->appendElement($string); return true; } } } return false; }
/** * Returns true if the current character is a curly bracket (}) which means * the end of the block. * * @param ezcTemplateCursor $cursor * @param ezcTemplateTstNode $operator * @param bool $finalize * @return bool */ public function atEnd(ezcTemplateCursor $cursor, $operator, $finalize = true) { if ($cursor->current(strlen($this->endBracket)) == $this->endBracket) { if (!$finalize) { return true; } // reached end of expression $cursor->advance(1); $this->block->endCursor = clone $this->block->endCursor; return true; } return false; }
/** * Parses the code by looking for start of expression blocks and then * passing control to the block parser (ezcTemplateBlockSourceToTstParser). The * text which is not covered by the block parser will be added as * text elements. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { $this->program = new ezcTemplateProgramTstNode($this->parser->source, $this->startCursor, $cursor); $this->lastBlock = $this->program; while (!$cursor->atEnd()) { // Find the first block $bracePosition = $cursor->findPosition("{", true); if ($bracePosition === false) { $cursor->gotoEnd(); // This will cause handleSuccessfulResult() to be called return true; } // Reached a block {...} $cursor->gotoPosition($bracePosition); $blockCursor = clone $cursor; $cursor->advance(1); if ($this->lastCursor->length($blockCursor) > 0) { $textElement = new ezcTemplateTextBlockTstNode($this->parser->source, clone $this->lastCursor, clone $blockCursor); $this->handleElements(array($textElement)); unset($textElement); } $this->startCursor->copy($blockCursor); $this->lastCursor->copy($cursor); if (!$this->parseRequiredType('Block', $this->startCursor, false)) { return false; } $this->startCursor->copy($cursor); $elements = $this->lastParser->elements; // Sanity checking to make sure element list does not contain duplicates, // this avoids having infinite recursions $count = count($elements); if ($count > 0) { $offset = 0; while ($offset < $count) { $element = $elements[$offset]; for ($i = $offset + 1; $i < $count; ++$i) { if ($element === $elements[$i]) { throw new ezcTemplateInternalException("Received element list with duplicate objects from parser " . get_class($this->lastParser)); } } ++$offset; } } $this->handleElements($elements); } // This will cause handleSuccessfulResult() to be called return true; }
/** * Parses the comment by looking for the end marker * + /. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { if (!$cursor->atEnd()) { $cursor->advance(2); $tagPos = $cursor->findPosition('*/'); if ($tagPos !== false) { // reached end of comment $cursor->gotoPosition($tagPos + 2); $commentBlock = new ezcTemplateBlockCommentTstNode($this->parser->source, $this->startCursor, clone $cursor); $commentBlock->commentText = substr($commentBlock->text(), 2, -2); $this->appendElement($commentBlock); return true; } } return false; }
/** * Parses the array types by looking for 'array(...)' and then using the * generic expression parser (ezcTemplateExpressionSourceToTstParser) to fetch the * keys and values. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { // skip whitespace and comments if (!$this->findNextElement()) { return false; } $name = $cursor->pregMatch("#^array[^\\w]#i", false); if ($name === false) { return false; } $lower = strtolower($name); if ($name !== $lower) { $this->findNonLowercase(); throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_ARRAY_NOT_LOWERCASE); } $cursor->advance(5); // skip whitespace and comments $this->findNextElement(); if (!$cursor->match('(')) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_ROUND_BRACKET_OPEN); } $currentArray = array(); $currentKeys = array(); $expectItem = true; $elementNumber = 0; while (true) { // skip whitespace and comments if (!$this->findNextElement()) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_ROUND_BRACKET_CLOSE); } if ($cursor->current() == ')') { $cursor->advance(); $array = new ezcTemplateLiteralArrayTstNode($this->parser->source, $this->startCursor, $cursor); $array->keys = $currentKeys; $array->value = $currentArray; $this->element = $array; $this->appendElement($array); return true; } if (!$expectItem) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_ROUND_BRACKET_CLOSE_OR_COMMA); } // Check for type if (!$expectItem || !$this->parseRequiredType('Expression')) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_LITERAL); } $this->findNextElement(); if ($cursor->match('=>')) { // Found the array key. Store it, and continue with the search for the value. $currentKeys[$elementNumber] = $this->lastParser->rootOperator; $this->findNextElement(); // We have the key => value syntax so we need to find the value if (!$this->parseRequiredType('Expression')) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_LITERAL); } // Store the value. $currentArray[$elementNumber] = $this->lastParser->rootOperator; $elementNumber++; } else { // Store the value. $currentArray[$elementNumber] = $this->lastParser->rootOperator; $elementNumber++; } if ($this->lastParser->rootOperator instanceof ezcTemplateModifyingOperatorTstNode) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_MODIFYING_EXPRESSION_NOT_ALLOWED); } $this->findNextElement(); // We allow a comma after the key/value even if there are no more // entries. This is compatible with PHP syntax. if ($cursor->match(',')) { $this->findNextElement(); $expectItem = true; } else { $expectItem = false; } } }
/** * Parses the block by using sub parser, the conditions are: * - The block contains {*...*} in which case ezcTemplateDocCommentSourceToTstParser is * used. * - The block contains a generic expression in which case * ezcTemplateExpressionBlockSourceToTstParser is used. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { // Check for doc comments which look like {*...*} if (!$cursor->atEnd() && $cursor->current() == '*') { // got a doc comment block if (!$this->parseRequiredType('DocComment', $this->startCursor)) { throw new ezcTemplateParserException($this->parser->source, $cursor, $cursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_CLOSING_BLOCK_COMMENT); } return true; } // $cursor object in $block will be updated as the parser continues $this->block = new ezcTemplateBlockTstNode($this->parser->source, $this->startCursor, $cursor); $this->findNextElement(); // Test for and ending control structure. if (!$cursor->atEnd() && $cursor->current() == '/') { // got a closing block marker $this->block->isClosingBlock = true; $closingCursor = clone $cursor; $this->block->closingCursor = $closingCursor; $cursor->advance(1); $this->findNextElement(); // Check for internal blocks which are known to not support closing markers. // foreach|while|if|switch|case|default|delimiter|literal|dynamic|cache_template $matches = $cursor->pregMatchComplete("#^(tr|tr_context|elseif|else|include|return|break|continue|skip|increment|decrement|reset|once|var|use|cycle|ldelim|rdelim)(?:[^a-zA-Z0-9_])#"); if ($matches !== false) { throw new ezcTemplateParserException($this->parser->source, $this->block->closingCursor, $this->block->closingCursor, ezcTemplateSourceToTstErrorMessages::MSG_CLOSING_BLOCK_NOW_ALLOWED); } } // Try to parse a control structure $controlStructureParser = new ezcTemplateControlStructureSourceToTstParser($this->parser, $this, null); $controlStructureParser->block = $this->block; if ($this->parseOptionalType($controlStructureParser, null, false)) { if ($this->lastParser->status == self::PARSE_PARTIAL_SUCCESS) { return false; } $this->mergeElements($this->lastParser); return true; } // Try to parse a literal block if ($this->parseOptionalType('LiteralBlock', $this->startCursor)) { return true; } // Try to parse a declaration block if ($this->parseOptionalType('DeclarationBlock', $this->startCursor)) { return true; } // Try to parse as an expression, if this fails the normal block parser // is tried. if ($this->parseOptionalType('ExpressionBlock', $this->startCursor)) { if (!$this->currentCursor->match('}')) { if ($this->currentCursor->match('[', false)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_UNEXPECTED_SQUARE_BRACKET_OPEN); } throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_CURLY_BRACKET_CLOSE); } return true; } if ($cursor->match('}')) { // Empty block found, this is allowed but the returned block // will be ignored when compiling $this->elements[] = $this->lastParser->block; return true; } // Parse the {ldelim} and {rdelim} $matches = $this->currentCursor->pregMatchComplete("#^(ldelim|rdelim)(?:[^a-zA-Z0-9_])#"); $name = $matches[1][0]; $ldelim = $name == 'ldelim' ? true : false; $rdelim = $name == 'rdelim' ? true : false; if ($ldelim || $rdelim) { $this->currentCursor->advance(strlen($name)); $this->findNextElement(); if (!$this->currentCursor->match("}")) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_CURLY_BRACKET_CLOSE); } $text = new ezcTemplateTextBlockTstNode($this->parser->source, $this->startCursor, $this->endCursor); $text->text = $ldelim ? "{" : "}"; $this->appendElement($text); return true; } // Parse the cache blocks. $cacheParser = new ezcTemplateCacheSourceToTstParser($this->parser, $this, null); $cacheParser->block = $this->block; if ($this->parseOptionalType($cacheParser, null)) { return true; } // Try to parse custom blocks, these are pluggable and follows a generic syntax. $customBlockParser = new ezcTemplateCustomBlockSourceToTstParser($this->parser, $this, null); $customBlockParser->block = $this->block; if ($this->parseOptionalType($customBlockParser, null)) { return true; } $matches = $cursor->pregMatchComplete("#^([a-zA-Z_][a-zA-Z0-9_-]*)(?:[^a-zA-Z])#i"); throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_UNKNOWN_BLOCK, $matches[1][0])); }
/** * Parses the expression by using the ezcTemplateExpressionSourceToTstParser class. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { if ($this->block->isClosingBlock) { $this->findNextElement(); $matches = $cursor->pregMatchComplete("#^([a-zA-Z_][a-zA-Z0-9_-]*)(?:[^a-zA-Z])#i"); if ($matches === false) { return false; } $name = $matches[1][0]; // If the custom block is not defined we have to return false. $def = $this->getCustomBlockDefinition($name); if ($def === false) { return false; } $cursor->advance(strlen($name)); $this->findNextElement($cursor); if (!$cursor->match("}")) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_CURLY_BRACKET_CLOSE); } $cb = new ezcTemplateCustomBlockTstNode($this->parser->source, $this->startCursor, $cursor); $cb->name = $name; $cb->isClosingBlock = true; $this->appendElement($cb); return true; } // Check for the name of the custom block // Note: The code inside the ( ?: ) brace ensures that the next character // is not an alphabetical character ie. a word boundary $matches = $cursor->pregMatchComplete("#^([a-zA-Z_][a-zA-Z0-9_-]*)(?:[^a-zA-Z])#i"); if ($matches === false) { return false; } $name = $matches[1][0]; $cursor->advance(strlen($name)); $this->findNextElement($cursor); // $def = ezcTemplateCustomBlockManager::getInstance()->getDefinition( $name ); $def = $this->getCustomBlockDefinition($name); if ($def === false) { return false; } $cb = new ezcTemplateCustomBlockTstNode($this->parser->source, $this->startCursor, $cursor); $cb->definition = $def; $cb->name = $name; $this->block->isNestingBlock = $cb->isNestingBlock = $def->hasCloseTag; $excessParameters = isset($def->excessParameters) && $def->excessParameters ? true : false; if (isset($def->startExpressionName) && $def->startExpressionName != "") { if (!in_array($def->startExpressionName, $def->optionalParameters) && !in_array($def->startExpressionName, $def->requiredParameters)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_REQUIRED_OR_OPTIONAL_PARAMETER_DEFINITION_IN_CUSTOM_BLOCK, $def->startExpressionName)); } if (!$this->parseOptionalType('Expression', null, false)) { if (in_array($def->startExpressionName, $def->requiredParameters)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_EXPRESSION); } } else { if ($this->lastParser->rootOperator instanceof ezcTemplateModifyingOperatorTstNode) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_MODIFYING_EXPRESSION_NOT_ALLOWED); } $cb->namedParameters[$def->startExpressionName] = $this->lastParser->rootOperator; $this->findNextElement($cursor); } } while (!$cursor->match("}")) { $match = $cursor->pregMatch("#^[a-zA-Z_][a-zA-Z0-9_-]*#"); if (!$match) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_UNEXPECTED_TOKEN, $cursor->current(1))); } if (!$excessParameters && !in_array($match, $def->optionalParameters) && !in_array($match, $def->requiredParameters)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_UNKNOWN_CUSTOM_BLOCK_PARAMETER, $match)); } if (array_key_exists($match, $cb->namedParameters)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_REASSIGNMENT_CUSTOM_BLOCK_PARAMETER, $match)); } $this->findNextElement($cursor); // The '=' is optional. if ($cursor->match("=")) { $this->findNextElement($cursor); } // The parameter has an expression. if (!$this->parseOptionalType('Expression', null, false)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_EXPRESSION); } if ($this->lastParser->rootOperator instanceof ezcTemplateModifyingOperatorTstNode) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, ezcTemplateSourceToTstErrorMessages::MSG_MODIFYING_EXPRESSION_NOT_ALLOWED); } // Append the parameter to the "namedParameters" array. $cb->namedParameters[$match] = $this->lastParser->rootOperator; } // Check if all requiredParameters are set. foreach ($def->requiredParameters as $val) { if (!array_key_exists($val, $cb->namedParameters)) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, sprintf(ezcTemplateSourceToTstErrorMessages::MSG_MISSING_CUSTOM_BLOCK_PARAMETER, $val)); } } $this->appendElement($cb); return true; }
/** * Parse operator * * @param ezcTemplateCursor $cursor * @param bool $canDoAssignment * @return bool */ protected function parseOperator($cursor, $canDoAssignment = true) { // Try som generic operators $operator = null; // This will contain the name of the operator if it is found. $operatorName = false; $operatorSymbols = array(array(3, array('===', '!==')), array(2, array('==', '!=', '<=', '>=', '&&', '||', '+=', '-=', '*=', '/=', '.=', '%=', '..', '=>')), array(1, array('+', '-', '.', '*', '/', '%', '<', '>', '='))); foreach ($operatorSymbols as $symbolEntry) { $chars = $cursor->current($symbolEntry[0]); if (in_array($chars, $symbolEntry[1])) { $operatorName = $chars; break; } } if ($operatorName !== false && $operatorName == "=>") { return false; } // Cannot do an assignment right now. if ($operatorName == "=" && !$canDoAssignment) { return false; } if ($operatorName !== false) { $operatorStartCursor = clone $cursor; $cursor->advance(strlen($operatorName)); $operatorMap = array('+' => 'PlusOperator', '-' => 'MinusOperator', '.' => 'ConcatOperator', '*' => 'MultiplicationOperator', '/' => 'DivisionOperator', '%' => 'ModuloOperator', '==' => 'EqualOperator', '!=' => 'NotEqualOperator', '===' => 'IdenticalOperator', '!==' => 'NotIdenticalOperator', '<' => 'LessThanOperator', '>' => 'GreaterThanOperator', '<=' => 'LessEqualOperator', '>=' => 'GreaterEqualOperator', '&&' => 'LogicalAndOperator', '||' => 'LogicalOrOperator', '=' => 'AssignmentOperator', '+=' => 'PlusAssignmentOperator', '-=' => 'MinusAssignmentOperator', '*=' => 'MultiplicationAssignmentOperator', '/=' => 'DivisionAssignmentOperator', '.=' => 'ConcatAssignmentOperator', '%=' => 'ModuloAssignmentOperator', '..' => 'ArrayRangeOperator'); $requestedName = $operatorName; $operatorName = $operatorMap[$operatorName]; $function = "ezcTemplate" . $operatorName . "TstNode"; $operator = new $function($this->parser->source, clone $this->lastCursor, $cursor); // If the min precedence has been reached we immediately stop parsing // and return a successful parse result if ($this->minPrecedence !== false && $operator->precedence < $this->minPrecedence) { $cursor->copy($operatorStartCursor); return $operatorName; } $this->checkForValidOperator($this->currentOperator, $operator, $operatorStartCursor); $this->currentOperator = $this->parser->handleOperatorPrecedence($this->currentOperator, $operator); $this->lastCursor->copy($cursor); return $operatorName; } return false; }
/** * Parses the literal by using the ezcTemplateLiteralParser class. * * @param ezcTemplateCursor $cursor * @return bool */ protected function parseCurrent(ezcTemplateCursor $cursor) { // $cursor will be update as the parser continues $this->block = new ezcTemplateLiteralBlockTstNode($this->parser->source, clone $this->startCursor, $cursor); // skip whitespace and comments if (!$this->findNextElement()) { return false; } $hasClosingMarker = $cursor->current() == '/'; if ($hasClosingMarker) { $closingCursor = clone $cursor; $cursor->advance(); $this->findNextElement(); } $matches = $cursor->pregMatchComplete("#^(literal)(?:[^a-zA-Z0-9_])#"); if ($matches === false) { return false; } $cursor->advance(strlen($matches[1][0])); // skip whitespace and comments if (!$this->findNextElement()) { return false; } // Assume end of first {literal} block if (!$cursor->match("}")) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $cursor, ezcTemplateSourceToTstErrorMessages::MSG_EXPECT_CURLY_BRACKET_CLOSE); } if ($hasClosingMarker) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $cursor, "Found closing block {/literal} without an opening block."); } $literalTextCursor = clone $cursor; // Start searching for ending literal block. while (!$cursor->atEnd()) { // Find the next block $tagPos = $cursor->findPosition("{"); if ($tagPos === false) { return false; } $tagCursor = clone $cursor; $tagCursor->gotoPosition($tagPos - 1); if ($tagCursor->current() == "\\") { // This means the tag is escaped and should be treated as text. $cursor->copy($tagCursor); $cursor->advance(2); unset($tagCursor); continue; } // Reached a block {...} $cursor->gotoPosition($tagPos); $literalTextEndCursor = clone $cursor; $cursor->advance(); $continue = false; while (!$cursor->atEnd()) { // skip whitespace and comments if (!$this->findNextElement()) { return false; } // Check for end, if not continue search if (!$cursor->match('/literal')) { $continue = true; break; } // skip whitespace and comments if (!$this->findNextElement()) { return false; } if ($cursor->current() == '}') { $this->block->textStartCursor = $literalTextCursor; $this->block->textEndCursor = $literalTextEndCursor; $cursor->advance(); $this->block->endCursor = clone $cursor; // Make sure the text is extracted now that the cursor are correct $this->block->storeText(); $this->appendElement($this->block); return true; } } if ($continue) { continue; } } return false; }