194 |
194 |
);
|
195 |
195 |
|
196 |
196 |
/**
|
|
197 |
* @var string
|
|
198 |
*/
|
|
199 |
protected $templatePathAndFilename = '';
|
|
200 |
|
|
201 |
/**
|
|
202 |
* @var integer
|
|
203 |
*/
|
|
204 |
protected $currentLineNumber = 0;
|
|
205 |
|
|
206 |
/**
|
|
207 |
* @var string
|
|
208 |
*/
|
|
209 |
protected $currentTemplateElement = '';
|
|
210 |
|
|
211 |
/**
|
197 |
212 |
* @var \F3\FLOW3\Object\FactoryInterface
|
198 |
213 |
*/
|
199 |
214 |
protected $objectFactory;
|
... | ... | |
219 |
234 |
}
|
220 |
235 |
|
221 |
236 |
/**
|
|
237 |
*
|
|
238 |
* @param string
|
|
239 |
* @return void
|
|
240 |
* @author Bastian Waidelich <bastian@typo3.org>
|
|
241 |
*/
|
|
242 |
public function setTemplatePathAndFilename($templatePathAndFilename) {
|
|
243 |
$this->templatePathAndFilename = $templatePathAndFilename;
|
|
244 |
}
|
|
245 |
|
|
246 |
/**
|
222 |
247 |
* Parses a given template and returns a parsed template object.
|
223 |
248 |
*
|
224 |
249 |
* @param string $templateString The template to parse as a string
|
... | ... | |
227 |
252 |
* @todo Refine doc comment
|
228 |
253 |
*/
|
229 |
254 |
public function parse($templateString) {
|
230 |
|
if (!is_string($templateString)) throw new \F3\Fluid\Core\Parser\Exception('Parse requires a template string as argument, ' . gettype($templateString) . ' given.', 1224237899);
|
|
255 |
if (!is_string($templateString)) throw new \InvalidArgumentException('Parse requires a template string as argument, ' . gettype($templateString) . ' given.', 1224237899);
|
231 |
256 |
|
232 |
|
$this->initialize();
|
233 |
|
|
234 |
257 |
$templateString = $this->extractNamespaceDefinitions($templateString);
|
235 |
258 |
$splittedTemplate = $this->splitTemplateAtDynamicTags($templateString);
|
236 |
259 |
$parsingState = $this->buildMainObjectTree($splittedTemplate);
|
... | ... | |
254 |
277 |
* @return void
|
255 |
278 |
* @author Sebastian Kurfürst <sebastian@typo3.org>
|
256 |
279 |
*/
|
257 |
|
protected function initialize() {
|
|
280 |
public function initialize() {
|
|
281 |
$this->templatePathAndFilename = '';
|
|
282 |
$this->currentLineNumber = 0;
|
|
283 |
$this->currentTemplateElement = '';
|
258 |
284 |
$this->namespaces = array(
|
259 |
285 |
'f' => 'F3\Fluid\ViewHelpers'
|
260 |
286 |
);
|
... | ... | |
274 |
300 |
$namespaceIdentifier = $matchedVariables[1][$index];
|
275 |
301 |
$fullyQualifiedNamespace = $matchedVariables[2][$index];
|
276 |
302 |
if (key_exists($namespaceIdentifier, $this->namespaces)) {
|
277 |
|
throw new \F3\Fluid\Core\Parser\Exception('Namespace identifier "' . $namespaceIdentifier . '" is already registered. Do not redeclare namespaces!', 1224241246);
|
|
303 |
$this->throwParserException('Namespace identifier "' . $namespaceIdentifier . '" is already registered. Do not redeclare namespaces!', 1224241246);
|
278 |
304 |
}
|
279 |
305 |
$this->namespaces[$namespaceIdentifier] = $fullyQualifiedNamespace;
|
280 |
306 |
}
|
... | ... | |
311 |
337 |
$rootNode = $this->objectFactory->create('F3\Fluid\Core\Parser\SyntaxTree\RootNode');
|
312 |
338 |
$state->setRootNode($rootNode);
|
313 |
339 |
$state->pushNodeToStack($rootNode);
|
|
340 |
$this->currentLineNumber = 1;
|
314 |
341 |
|
315 |
342 |
foreach ($splittedTemplate as $templateElement) {
|
|
343 |
if (\F3\Fluid\Fluid::$debugMode) {
|
|
344 |
$this->currentTemplateElement = $templateElement;
|
|
345 |
$this->currentLineNumber += $this->countLines($templateElement);
|
|
346 |
}
|
316 |
347 |
$matchedVariables = array();
|
317 |
348 |
if (preg_match(self::$SCAN_PATTERN_CDATA, $templateElement, $matchedVariables) > 0) {
|
318 |
349 |
$this->handler_text($state, $matchedVariables[1]);
|
... | ... | |
334 |
365 |
}
|
335 |
366 |
|
336 |
367 |
if ($state->countNodeStack() !== 1) {
|
337 |
|
throw new \F3\Fluid\Core\Parser\Exception('Not all tags were closed!', 1238169398);
|
|
368 |
$this->throwParserException('Not all tags were closed!', 1238169398);
|
338 |
369 |
}
|
339 |
370 |
return $state;
|
340 |
371 |
}
|
341 |
372 |
|
342 |
373 |
/**
|
|
374 |
* Counts the number of lines in a given string
|
|
375 |
*
|
|
376 |
* @param string $templateCode
|
|
377 |
* @return integer number of lines in this string
|
|
378 |
* @author Bastian Waidelich <bastian@typo3.org>
|
|
379 |
*/
|
|
380 |
protected function countLines($templateCode) {
|
|
381 |
$lines = substr_count($templateCode, chr(10));
|
|
382 |
return $lines;
|
|
383 |
}
|
|
384 |
|
|
385 |
/**
|
343 |
386 |
* Handles an opening or self-closing view helper tag.
|
344 |
387 |
*
|
345 |
388 |
* @param \F3\Fluid\Core\Parser\ParsingState $state Current parsing state
|
... | ... | |
352 |
395 |
*/
|
353 |
396 |
protected function handler_openingViewHelperTag(\F3\Fluid\Core\Parser\ParsingState $state, $namespaceIdentifier, $methodIdentifier, $arguments, $selfclosing) {
|
354 |
397 |
if (!array_key_exists($namespaceIdentifier, $this->namespaces)) {
|
355 |
|
throw new \F3\Fluid\Core\Parser\Exception('Namespace could not be resolved. This exception should never be thrown!', 1224254792);
|
|
398 |
$this->throwParserException('Namespace could not be resolved. This exception should never be thrown!', 1224254792);
|
356 |
399 |
}
|
357 |
400 |
|
358 |
401 |
$argumentsObjectTree = $this->parseArguments($arguments);
|
359 |
402 |
$viewHelperName = $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier);
|
360 |
403 |
|
361 |
|
$viewHelper = $this->objectFactory->create($viewHelperName);
|
|
404 |
try {
|
|
405 |
$viewHelper = $this->objectFactory->create($viewHelperName);
|
|
406 |
} catch (\F3\FLOW3\Object\Exception\UnknownObject $exception) {
|
|
407 |
$this->throwParserException('View helper "' . $viewHelperName . '" does not exist', 1252850133, $exception);
|
|
408 |
}
|
362 |
409 |
$expectedViewHelperArguments = $viewHelper->prepareArguments();
|
363 |
410 |
$this->abortIfUnregisteredArgumentsExist($expectedViewHelperArguments, $argumentsObjectTree);
|
364 |
411 |
$this->abortIfRequiredArgumentsAreMissing($expectedViewHelperArguments, $argumentsObjectTree);
|
... | ... | |
392 |
439 |
|
393 |
440 |
foreach (array_keys($actualArguments) as $argumentName) {
|
394 |
441 |
if (!in_array($argumentName, $expectedArgumentNames)) {
|
395 |
|
throw new \F3\Fluid\Core\Parser\Exception('Argument "' . $argumentName . '" was not registered.', 1237823695);
|
|
442 |
$this->throwParserException('Argument "' . $argumentName . '" was not registered.', 1237823695);
|
396 |
443 |
}
|
397 |
444 |
}
|
398 |
445 |
}
|
... | ... | |
408 |
455 |
$actualArgumentNames = array_keys($actualArguments);
|
409 |
456 |
foreach ($expectedArguments as $expectedArgument) {
|
410 |
457 |
if ($expectedArgument->isRequired() && !in_array($expectedArgument->getName(), $actualArgumentNames)) {
|
411 |
|
throw new \F3\Fluid\Core\Parser\Exception('Required argument "' . $expectedArgument->getName() . '" was not supplied.', 1237823699);
|
|
458 |
$this->throwParserException('Required argument "' . $expectedArgument->getName() . '" was not supplied.', 1237823699);
|
412 |
459 |
}
|
413 |
460 |
}
|
414 |
461 |
}
|
... | ... | |
447 |
494 |
*/
|
448 |
495 |
protected function handler_closingViewHelperTag(\F3\Fluid\Core\Parser\ParsingState $state, $namespaceIdentifier, $methodIdentifier) {
|
449 |
496 |
if (!array_key_exists($namespaceIdentifier, $this->namespaces)) {
|
450 |
|
throw new \F3\Fluid\Core\Parser\Exception('Namespace could not be resolved. This exception should never be thrown!', 1224256186);
|
|
497 |
$this->throwParserException('Namespace could not be resolved. This exception should never be thrown!', 1224256186);
|
451 |
498 |
}
|
452 |
499 |
$lastStackElement = $state->popNodeFromStack();
|
453 |
500 |
if (!($lastStackElement instanceof \F3\Fluid\Core\Parser\SyntaxTree\ViewHelperNode)) {
|
454 |
|
throw new \F3\Fluid\Core\Parser\Exception('You closed a templating tag which you never opened!', 1224485838);
|
|
501 |
$this->throwParserException('You closed a templating tag which you never opened!', 1224485838);
|
455 |
502 |
}
|
456 |
503 |
if ($lastStackElement->getViewHelperClassName() != $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier)) {
|
457 |
|
throw new \F3\Fluid\Core\Parser\Exception('Templating tags not properly nested. Expected: ' . $lastStackElement->getViewHelperClassName() . '; Actual: ' . $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier), 1224485398);
|
|
504 |
$this->throwParserException('Templating tags not properly nested. Expected: ' . $lastStackElement->getViewHelperClassName() . '; Actual: ' . $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier), 1224485398);
|
458 |
505 |
}
|
459 |
506 |
}
|
460 |
507 |
|
... | ... | |
504 |
551 |
* @param string $argumentString
|
505 |
552 |
* @return ArgumentObject the corresponding argument object tree.
|
506 |
553 |
* @author Sebastian Kurfürst <sebastian@typo3.org>
|
|
554 |
* @author Bastian Waidelich <bastian@typo3.org>
|
507 |
555 |
*/
|
508 |
556 |
protected function buildArgumentObjectTree($argumentString) {
|
509 |
557 |
$splittedArgument = $this->splitTemplateAtDynamicTags($argumentString);
|
510 |
|
$rootNode = $this->buildMainObjectTree($splittedArgument)->getRootNode();
|
|
558 |
|
|
559 |
$state = $this->objectFactory->create('F3\Fluid\Core\Parser\ParsingState');
|
|
560 |
$rootNode = $this->objectFactory->create('F3\Fluid\Core\Parser\SyntaxTree\RootNode');
|
|
561 |
$state->setRootNode($rootNode);
|
|
562 |
$state->pushNodeToStack($rootNode);
|
|
563 |
|
|
564 |
foreach ($splittedArgument as $templateElement) {
|
|
565 |
$this->handler_textAndShorthandSyntax($state, $templateElement);
|
|
566 |
}
|
|
567 |
|
511 |
568 |
return $rootNode;
|
512 |
569 |
}
|
513 |
570 |
|
... | ... | |
624 |
681 |
} elseif ( array_key_exists('Subarray', $singleMatch) && !empty($singleMatch['Subarray'])) {
|
625 |
682 |
$arrayToBuild[$arrayKey] = $this->handler_array_recursively($singleMatch['Subarray']);
|
626 |
683 |
} else {
|
627 |
|
throw new \F3\Fluid\Core\Parser\Exception('This exception should never be thrown, as the array value has to be of some type (Value given: "' . var_export($singleMatch, TRUE) . '"). Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
|
|
684 |
$this->throwParserException('This exception should never be thrown, as the array value has to be of some type (Value given: "' . var_export($singleMatch, TRUE) . '"). Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
|
628 |
685 |
}
|
629 |
686 |
}
|
630 |
687 |
return $this->objectFactory->create('F3\Fluid\Core\Parser\SyntaxTree\ArrayNode', $arrayToBuild);
|
631 |
688 |
} else {
|
632 |
|
throw new \F3\Fluid\Core\Parser\Exception('This exception should never be thrown, there is most likely some error in the regular expressions. Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
|
|
689 |
$this->throwParserException('This exception should never be thrown, there is most likely some error in the regular expressions. Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
|
633 |
690 |
}
|
634 |
691 |
}
|
635 |
692 |
|
... | ... | |
645 |
702 |
$node = $this->objectFactory->create('F3\Fluid\Core\Parser\SyntaxTree\TextNode', $text);
|
646 |
703 |
$state->getNodeFromStack()->addChildNode($node);
|
647 |
704 |
}
|
|
705 |
|
|
706 |
/**
|
|
707 |
* Throws a \F3\Fluid\Core\Parser\Exception. The given message is extended by the current template extract.
|
|
708 |
*
|
|
709 |
* @param string $message the exception message
|
|
710 |
* @param integer $code the exception code
|
|
711 |
* @param \Exception the inner exception
|
|
712 |
* @return void
|
|
713 |
* @author Bastian Waidelich <bastian@typo3.org>
|
|
714 |
*/
|
|
715 |
protected function throwParserException($message, $code, \Exception $previousException = NULL) {
|
|
716 |
if (\F3\Fluid\Fluid::$debugMode) {
|
|
717 |
$templateLines = file($this->templatePathAndFilename);
|
|
718 |
$truncatedTemplatePathAndFilename = substr($this->templatePathAndFilename, strlen(FLOW3_PATH_FLOW3));
|
|
719 |
$message .= ' in template "' . $truncatedTemplatePathAndFilename . '"';
|
|
720 |
$currentTemplateLine = isset($templateLines[$this->currentLineNumber - 1]) ? $templateLines[$this->currentLineNumber - 1] : NULL;
|
|
721 |
if ($currentTemplateLine !== NULL) {
|
|
722 |
$escapedTemplateLine = trim(htmlspecialchars($currentTemplateLine));
|
|
723 |
$escapedTemplateElement = htmlspecialchars($this->currentTemplateElement);
|
|
724 |
$escapedTemplateLine = str_replace($escapedTemplateElement, '<span style="color: #BE0027;">' . $escapedTemplateElement . '</span>', $escapedTemplateLine);
|
|
725 |
$message .= ', line ' . $this->currentLineNumber . ': <pre>' . $escapedTemplateLine . '</pre>';
|
|
726 |
}
|
|
727 |
}
|
|
728 |
throw new \F3\Fluid\Core\Parser\Exception($message, $code, $previousException);
|
|
729 |
}
|
648 |
730 |
}
|
649 |
731 |
?>
|