- Added basic exception remodeling, seaking original template sources.

This commit is contained in:
Dave M. 2020-10-21 03:20:35 +00:00
parent 10632e9f67
commit 1334278e37
7 changed files with 232 additions and 57 deletions

View File

@ -22,6 +22,8 @@ class Builder
'%USE%' => ( $uses = $context->renderUses() ) ? "use $uses;" : false, '%USE%' => ( $uses = $context->renderUses() ) ? "use $uses;" : false,
'%CLASSNAME%' => $context->className, '%CLASSNAME%' => $context->className,
'%PATHNAME%' => $context->viewPath, '%PATHNAME%' => $context->viewPath,
'%FULLPATH%' => $context->filePath,
'%TEMPLATE%' => $this->templatePath,
'%EXTENDS%' => $context->extendFrom ? "extends " . static::TEMPLATE_CLASSNAME_PREFIX . static::generateClassUID($context->extendFrom) : '', '%EXTENDS%' => $context->extendFrom ? "extends " . static::TEMPLATE_CLASSNAME_PREFIX . static::generateClassUID($context->extendFrom) : '',
'%EXTENDS_TEMPLATE%' => $context->extendFrom, '%EXTENDS_TEMPLATE%' => $context->extendFrom,
'%CONTENT%' => $compiledSource, '%CONTENT%' => $compiledSource,

View File

@ -5,7 +5,10 @@ namespace %NAMESPACE%;
%USE% %USE%
# %PATHNAME% # %PATHNAME%
class %CLASSNAME% %EXTENDS% {
if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
class %CLASSNAME% %EXTENDS% {
public array $blockList = []; public array $blockList = [];
public array $sectionList = []; public array $sectionList = [];
@ -58,12 +61,29 @@ class %CLASSNAME% %EXTENDS% {
%FUNCTIONS% %FUNCTIONS%
} }
public function getSourceLineFromException(\Throwable $ex) : ? int
{
$sourceFile = file_get_contents("%TEMPLATE%");
if ( $sourceFile ) {
foreach(explode(PHP_EOL, $sourceFile) as $line => $content) {
if ( strpos($content, str_replace('$', '%', '$CONTENT$')) !== false ) {
return $ex->getLine() - $line;
}
}
}
return null;
}
public function __invoke(array $variablesList = []) : string public function __invoke(array $variablesList = []) : string
{ {
ob_start(); ob_start();
$this->output($variablesList); $this->output($variablesList);
return ob_get_clean(); return ob_get_clean();
} }
}
} }
return [ 'classname' => "%CLASSNAME%", 'namespace' => "%NAMESPACE%", 'extends' => "%EXTENDS_TEMPLATE%" ]; return [ 'classname' => "%CLASSNAME%", 'namespace' => "%NAMESPACE%", 'extends' => "%EXTENDS_TEMPLATE%", 'view' => "%FULLPATH%" ];

View File

@ -45,8 +45,6 @@ class Compiler
$this->sourceCode = preg_replace_callback($replace, function ($matches) use (&$context) { $this->sourceCode = preg_replace_callback($replace, function ($matches) use (&$context) {
$matches[2] = trim($matches[2]); $matches[2] = trim($matches[2]);
list($token, $arguments) = array_pad(array_filter(explode(' ', $matches[2], 2), 'strlen'), 2, null); list($token, $arguments) = array_pad(array_filter(explode(' ', $matches[2], 2), 'strlen'), 2, null);
$token = trim($token); $token = trim($token);

View File

@ -45,9 +45,9 @@ class SectionToken implements ControlStructure {
$order = $options['order'] ?? "count(\$___class__template->sectionList[$name]['$action'])"; $order = $options['order'] ?? "count(\$___class__template->sectionList[$name]['$action'])";
return "<?php \$___class__template->sectionList[$name] ??= [ 'prepend' => [], 'append' => [], 'default' => [] ]; return "<?php \$___class__template->sectionList[$name] ??= [ 'prepend' => [], 'append' => [], 'default' => [] ];".
\$___class__template->sectionList[$name]['$action'][] = [ 'order' => $order, 'callback' => function() use (\$picea, \$___class__template, \$___global_variables, \$___variables) { "\$___class__template->sectionList[$name]['$action'][] = [ 'order' => $order, 'callback' => function() use (\$picea, \$___class__template, \$___global_variables, \$___variables) {".
extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); ?>"; "extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); ?>";
} }
protected function printEndSection($context) : string protected function printEndSection($context) : string

View File

@ -0,0 +1,129 @@
<?php
namespace Twig\Error;
use Twig\Source;
use Twig\Template;
/**
* Twig base exception.
*
* This exception class and its children must only be used when
* an error occurs during the loading of a template, when a syntax error
* is detected in a template, or when rendering a template. Other
* errors must use regular PHP exception classes (like when the template
* cache directory is not writable for instance).
*
* To help debugging template issues, this class tracks the original template
* name and line where the error occurred.
*
* Whenever possible, you must set these information (original template name
* and line number) yourself by passing them to the constructor. If some or all
* these information are not available from where you throw the exception, then
* this class will guess them automatically (when the line number is set to -1
* and/or the name is set to null). As this is a costly operation, this
* can be disabled by passing false for both the name and the line number
* when creating a new instance of this class.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RenderingError extends \Exception
{
/**
* Constructor.
*
* By default, automatic guessing is enabled.
*
* @param string $message The error message
* @param int $lineno The template line where the error occurred
* @param Source|null $source The source context where the error occurred
*/
public function __construct(string $message, Source $source = null, \Exception $previous = null)
{
parent::__construct('', 0, $previous);
if (null === $source) {
$name = null;
} else {
$name = $source->getName();
$this->sourceCode = $source->getCode();
$this->sourcePath = $source->getPath();
}
$this->lineno = $lineno;
$this->name = $name;
$this->rawMessage = $message;
}
public static function generateFromCompiledClass(\Throwable $exception) : self
{
return $exception;
}
private function readTemplateInformation(): void
{
$template = null;
$templateClass = null;
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Template) {
$currentClass = \get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = \get_class($trace['object']);
}
}
}
// update template name
if (null !== $template && null === $this->name) {
$this->name = $template->getTemplateName();
}
// update template path if any
if (null !== $template && null === $this->sourcePath) {
$src = $template->getSourceContext();
$this->sourceCode = $src->getCode();
$this->sourcePath = $src->getPath();
}
if (null === $template || $this->lineno > -1) {
return;
}
$r = new \ReflectionObject($template);
$file = $r->getFileName();
$exceptions = [$e = $this];
while ($e = $e->getPrevious()) {
$exceptions[] = $e;
}
while ($e = array_pop($exceptions)) {
$traces = $e->getTrace();
array_unshift($traces, ['file' => $e->getFile(), 'line' => $e->getLine()]);
while ($trace = array_shift($traces)) {
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
continue;
}
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
if ($codeLine <= $trace['line']) {
// update template line
$this->lineno = $templateLine;
return;
}
}
}
}
}
}

View File

@ -53,8 +53,13 @@ class FileFetcher
throw new \RuntimeException("Given view file `$fileName` can not be found within given folder list.."); throw new \RuntimeException("Given view file `$fileName` can not be found within given folder list..");
} }
public function getFilePath(string $fileName) : string
{
return $this->findFile($fileName);
}
public function getFileContent(string $fileName) : string public function getFileContent(string $fileName) : string
{ {
return file_get_contents($this->findFile($fileName)); return file_get_contents($this->getFilePath($fileName));
} }
} }

View File

@ -3,7 +3,9 @@
namespace Picea; namespace Picea;
use Closure; use Closure;
use Picea\Language\LanguageRegistration; use Picea\Language\LanguageRegistration,
Picea\Exception\RenderingError;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
class Picea implements LanguageRegistration class Picea implements LanguageRegistration
@ -49,14 +51,32 @@ class Picea implements LanguageRegistration
$this->renderContext($this->context); $this->renderContext($this->context);
} }
public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : string public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : ? string
{ {
if ( null === $object = $this->fetchFromCache($viewPath, $variables, $proxy) ) { if ( null === $object = $this->fetchFromCache($viewPath, $variables, $proxy) ) {
throw new \RuntimeException("An error occured while trying to save a compiled template."); throw new \RuntimeException("An error occured while trying to save a compiled template.");
} }
try {
return $object(); return $object();
} }
catch(\Throwable $ex) {
# Temporary class for an experiment
throw new class($object, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex) extends \Exception {
public function __construct(object $compiledObject, string $message, int $code, \Throwable $previous)
{
parent::__construct($message, $code, $previous);
$classContent = include( $previous->getFile() );
if ( is_array($classContent) ) {
$this->file = $classContent['view'];
$this->line = $compiledObject->getSourceLineFromException($previous);
}
}
};
}
}
/** /**
* Method used by Block and View tokens * Method used by Block and View tokens
@ -207,6 +227,7 @@ class Picea implements LanguageRegistration
$compiled = $this->compileSource($this->fileFetcher->getFileContent($viewPath)); $compiled = $this->compileSource($this->fileFetcher->getFileContent($viewPath));
$context = $compiled['context']; $context = $compiled['context'];
$context->viewPath = $viewPath; $context->viewPath = $viewPath;
$context->filePath = $this->fileFetcher->getFilePath($viewPath);
$context = $builder->build($compiled['context'], $compiled['source']) ; $context = $builder->build($compiled['context'], $compiled['source']) ;
$context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->className . ".php"; $context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->className . ".php";