- Added basic exception remodeling, seaking original template sources.
This commit is contained in:
parent
10632e9f67
commit
1334278e37
|
@ -22,6 +22,8 @@ class Builder
|
|||
'%USE%' => ( $uses = $context->renderUses() ) ? "use $uses;" : false,
|
||||
'%CLASSNAME%' => $context->className,
|
||||
'%PATHNAME%' => $context->viewPath,
|
||||
'%FULLPATH%' => $context->filePath,
|
||||
'%TEMPLATE%' => $this->templatePath,
|
||||
'%EXTENDS%' => $context->extendFrom ? "extends " . static::TEMPLATE_CLASSNAME_PREFIX . static::generateClassUID($context->extendFrom) : '',
|
||||
'%EXTENDS_TEMPLATE%' => $context->extendFrom,
|
||||
'%CONTENT%' => $compiledSource,
|
||||
|
|
|
@ -5,65 +5,85 @@ namespace %NAMESPACE%;
|
|||
%USE%
|
||||
|
||||
# %PATHNAME%
|
||||
class %CLASSNAME% %EXTENDS% {
|
||||
public array $blockList = [];
|
||||
|
||||
public array $sectionList = [];
|
||||
if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
|
||||
|
||||
public array $variableList = [];
|
||||
class %CLASSNAME% %EXTENDS% {
|
||||
public array $blockList = [];
|
||||
|
||||
public ?object $thisProxy = null;
|
||||
public array $sectionList = [];
|
||||
|
||||
public \Picea\Picea $picea;
|
||||
public array $variableList = [];
|
||||
|
||||
public static $context;
|
||||
|
||||
public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) {
|
||||
$this->picea = $picea;
|
||||
$this->variableList = $variablesList;
|
||||
$this->thisProxy = $thisProxy;
|
||||
$this->exportFunctions();
|
||||
|
||||
static::$context = $picea->context;
|
||||
}
|
||||
public ?object $thisProxy = null;
|
||||
|
||||
public function output(array $variablesList = []) : void
|
||||
{
|
||||
( function($___class__template, $___global_variables, $___variables, $picea) {
|
||||
extract($___global_variables);
|
||||
extract($___variables, \EXTR_OVERWRITE);
|
||||
?>%CONTENT%<?php
|
||||
} )->call($this->thisProxy ?? new class(){}, $this, $this->variableList, $variablesList, $this->picea);
|
||||
%PARENT_OUTPUT%
|
||||
}
|
||||
public \Picea\Picea $picea;
|
||||
|
||||
public function renderSection($name) : void
|
||||
{
|
||||
foreach([ 'prepend', 'default', 'append' ] as $item) {
|
||||
usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']);
|
||||
public static $context;
|
||||
|
||||
foreach($this->sectionList[$name][$item] as $section) {
|
||||
$section['callback']();
|
||||
public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) {
|
||||
$this->picea = $picea;
|
||||
$this->variableList = $variablesList;
|
||||
$this->thisProxy = $thisProxy;
|
||||
$this->exportFunctions();
|
||||
|
||||
if ( $item === 'default' ) {
|
||||
break 1;
|
||||
static::$context = $picea->context;
|
||||
}
|
||||
|
||||
public function output(array $variablesList = []) : void
|
||||
{
|
||||
( function($___class__template, $___global_variables, $___variables, $picea) {
|
||||
extract($___global_variables);
|
||||
extract($___variables, \EXTR_OVERWRITE);
|
||||
?>%CONTENT%<?php
|
||||
} )->call($this->thisProxy ?? new class(){}, $this, $this->variableList, $variablesList, $this->picea);
|
||||
%PARENT_OUTPUT%
|
||||
}
|
||||
|
||||
public function renderSection($name) : void
|
||||
{
|
||||
foreach([ 'prepend', 'default', 'append' ] as $item) {
|
||||
usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']);
|
||||
|
||||
foreach($this->sectionList[$name][$item] as $section) {
|
||||
$section['callback']();
|
||||
|
||||
if ( $item === 'default' ) {
|
||||
break 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function exportFunctions() : void
|
||||
{
|
||||
static $caching = [];
|
||||
%FUNCTIONS%
|
||||
|
||||
public function exportFunctions() : void
|
||||
{
|
||||
static $caching = [];
|
||||
%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
|
||||
{
|
||||
ob_start();
|
||||
$this->output($variablesList);
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(array $variablesList = []) : string
|
||||
{
|
||||
ob_start();
|
||||
$this->output($variablesList);
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
return [ 'classname' => "%CLASSNAME%", 'namespace' => "%NAMESPACE%", 'extends' => "%EXTENDS_TEMPLATE%" ];
|
||||
return [ 'classname' => "%CLASSNAME%", 'namespace' => "%NAMESPACE%", 'extends' => "%EXTENDS_TEMPLATE%", 'view' => "%FULLPATH%" ];
|
||||
|
|
|
@ -45,8 +45,6 @@ class Compiler
|
|||
$this->sourceCode = preg_replace_callback($replace, function ($matches) use (&$context) {
|
||||
$matches[2] = trim($matches[2]);
|
||||
|
||||
|
||||
|
||||
list($token, $arguments) = array_pad(array_filter(explode(' ', $matches[2], 2), 'strlen'), 2, null);
|
||||
|
||||
$token = trim($token);
|
||||
|
|
|
@ -45,9 +45,9 @@ class SectionToken implements ControlStructure {
|
|||
|
||||
$order = $options['order'] ?? "count(\$___class__template->sectionList[$name]['$action'])";
|
||||
|
||||
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) {
|
||||
extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); ?>";
|
||||
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) {".
|
||||
"extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); ?>";
|
||||
}
|
||||
|
||||
protected function printEndSection($context) : string
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,8 +53,13 @@ class FileFetcher
|
|||
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
|
||||
{
|
||||
return file_get_contents($this->findFile($fileName));
|
||||
return file_get_contents($this->getFilePath($fileName));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
namespace Picea;
|
||||
|
||||
use Closure;
|
||||
use Picea\Language\LanguageRegistration;
|
||||
use Picea\Language\LanguageRegistration,
|
||||
Picea\Exception\RenderingError;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class Picea implements LanguageRegistration
|
||||
|
@ -49,15 +51,33 @@ class Picea implements LanguageRegistration
|
|||
$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) ) {
|
||||
throw new \RuntimeException("An error occured while trying to save a compiled template.");
|
||||
}
|
||||
|
||||
return $object();
|
||||
try {
|
||||
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
|
||||
* @param object $proxy
|
||||
|
@ -68,7 +88,7 @@ class Picea implements LanguageRegistration
|
|||
public function inlineHtml(? object $proxy, string $viewPath, ... $variables) {
|
||||
return $this->renderHtml($viewPath, [ 'inlineVariables' => $variables ], $proxy);
|
||||
}
|
||||
|
||||
|
||||
public function renderContext(Compiler\Context $context) : object
|
||||
{
|
||||
if ( null === $object = $this->contextFromCache($context) ) {
|
||||
|
@ -207,6 +227,7 @@ class Picea implements LanguageRegistration
|
|||
$compiled = $this->compileSource($this->fileFetcher->getFileContent($viewPath));
|
||||
$context = $compiled['context'];
|
||||
$context->viewPath = $viewPath;
|
||||
$context->filePath = $this->fileFetcher->getFilePath($viewPath);
|
||||
$context = $builder->build($compiled['context'], $compiled['source']) ;
|
||||
$context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->className . ".php";
|
||||
|
||||
|
|
Loading…
Reference in New Issue