4704_Improve_parsing_exception_messages.patch

Bastian Waidelich, 2009-09-20 16:47

Download (11.7 KB)

View differences:

Classes/Core/Parser/TemplateParser.php (working copy)
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
?>
Classes/View/TemplateView.php (working copy)
352 352
	 */
353 353
	protected function parseTemplate($templatePathAndFilename) {
354 354
		$templateSource = \F3\FLOW3\Utility\Files::getFileContents($templatePathAndFilename, FILE_TEXT);
355
		$this->templateParser->initialize();
356
		$this->templateParser->setTemplatePathAndFilename($templatePathAndFilename);
355 357

  
356 358
		return $this->templateParser->parse($templateSource);
357 359
	}