diff --git a/docs/00-intro.md b/docs/00-intro.md new file mode 100644 index 0000000..246a8bc --- /dev/null +++ b/docs/00-intro.md @@ -0,0 +1,47 @@ +# Picea + +Welcome to the official Picea documentation. + +This library offers modern features from templating language, while simply generating normal PHP after compilation, such as view inheritance, definable blocks, 'or' token on unrunned looped, etc... + +Picea uses the same delimiters that Twig uses, which are `{% %}`, `{{ }}` and `{# #}`. + +The first `{% %}` is used for most **control structure** and **extensions**. + +The `{{ }}` delimiter is used to **echo escaped content** in a page. (see *01-echoing*) + +The `{# #}` is exclusively used as a **comment** enclosure. (see *01-comment*) + +## Quick start + +Render a simple Picea view: + +```php +$picea = new Picea\Picea(); +$picea->renderHtml('view/path/hello_world'); +``` + +And the view content could look like: + +*path/hello_world* +```html + + + + Picea's simple + + + + + {{ $someText }} + + +``` + + diff --git a/docs/01-comment.md b/docs/01-comment.md new file mode 100644 index 0000000..b2be90f --- /dev/null +++ b/docs/01-comment.md @@ -0,0 +1,26 @@ +# Control structures - Comments + +There is no single-line token to comment code within Picea, it's a tag working as such : + +**[PICEA]** So, using this code: + +```html +{# This is how you comment in a Picea view #} + +
+ {# Is it hello or good bye ? not sure on this ! #} + Good bye world ! +
+ +{# This is a multi-line comment +
This part will not be rendered
+#} +``` + +**[HTML]** Would render as such: + +```html +
+ Good bye world ! +
+``` \ No newline at end of file diff --git a/docs/01-echoing.md b/docs/01-echoing.md new file mode 100644 index 0000000..b5510ab --- /dev/null +++ b/docs/01-echoing.md @@ -0,0 +1,73 @@ +# Echoing text or data + +There is two token groups that you can use to print content into a view, `echo` or `print`. + +Escaped by default : `{% echo $argument %}` = `{{ $argument }}` = `{% print $argument %}` + +Raw when needed: `{% echo.raw $argument %}` = `{{= $argument }}` = `{% print.raw $argument %}` + +## Outputing content to a view using `echo` / `{{ }}` / `print`, `echo.raw` / `{{= }}` / `print.raw` + +Using `print` or `echo`, which are, by default, made safer by using PHP's native `htmlspecialchars`. + +**[PICEA]** So, using this code: + +```html +{{ "Hello World !" }} + +{% echo "This is another way to output content" %} + +{% print "This is the third way to output content" %} + +
+ +{% echo.raw $someHTML %} + +{% print.raw $someOtherHTML %} + +{{= $someMoreHTML }} +``` + +**[PHP]** Would yield internally: + +```php + + + + + + +
+ + + + + + +``` + +## Using string format variant `printf` / `printf.raw` + +Those tokens represents the equivalent of the printf() function from PHP. + +**[PICEA]** So, using this code: + +```html +{% php + $num = 5; + $location = 'tree'; +%} +{% printf 'There are %d monkeys in the %s', $num, $location %} +{% printf.raw 'There are %d monkeys in the %s', $num, $location %} +``` + +**[PHP]** Would render internally as : + +```php + + + +``` \ No newline at end of file diff --git a/docs/02-control-structure-comparisons.md b/docs/02-control-structure-comparisons.md new file mode 100644 index 0000000..4afb49a --- /dev/null +++ b/docs/02-control-structure-comparisons.md @@ -0,0 +1,86 @@ +# Control structure - Comparisons + +Most control structures used within PHP view templates are supported natively in Picea. + +The goal for this project always has been to not loose original feature while removing some limitations with PHP's original templating syntax. + +## Comparison using `if` / `then` / `else` / `elseif` / `endif` + +Comparisons works the same way as using PHP's alternative syntax. + +This is also how they are rendered in the compilation process. + +**[PICEA]** So, using this code: + +```html +{% php $search = 'Pack my box with five dozen liquor jugs' %} + +{% if strpos($search, 'beer') !== false %} + Found 'beer' into {{ $search }} ! +{% elseif strpos($search, 'liquor') !== false %} + Found 'liquor' into {{ $search }} ! +{% else %} + Neither 'beer' or 'liquor' were found in {{ $search }} +{% endif %} +``` + +**[PHP]** Would yield: + +```php + + + + Found 'beer' into ! + + Found 'liquor' into ! + + Neither 'beer' or 'liquor' were found in + +``` + +And then, the code would be runned through PHP's native `include` mechanic. + +## Comparison using `switch` / `case` / `break` / `endswitch` + +Using switches within HTML in plain PHP can be quite cumbersome because of it's limitation +disallowing any output (including whitespace) between it's statement. + +Picea will allow some more complex (and readable) switches within your views. + +**[PICEA]** So, using this code: + +```php +{% php $selected = random_int(0,5) %} + +{% switch $selected %} + +{% case 1 %} + {{ "One person selected" }} +{% break %} + +{% case 2 %} +{% default %} + {{ "Multiple person ($selected) selected" }} +{% break %} + +{% endswitch %} +``` + +**[PHP]** Would render as such: + +```php + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/docs/02-control-structure-extends-section.md b/docs/02-control-structure-extends-section.md new file mode 100644 index 0000000..ef1b1ad --- /dev/null +++ b/docs/02-control-structure-extends-section.md @@ -0,0 +1,122 @@ +# Control structure - `extends` / `section` + +A nice feature of most robust templating engine is the ability to inherit from other view. + +Picea follows a similar train of tought, since it's also possible create a chain of inheritance +using `extends` which have definable parts you can declare using `section`. + +## Basic `extends` (string $path) + +You must provide a valid `$path` from which the template will be inherited. + +**[PICEA]** So, using this code: + +*path/base/layout.phtml* +```html + + + + {% section "head" %} + + + {{ title() }} - AnExampleApp + {% section %} + + + +
{% section "main" %}{% endsection %}
+
+ {% section "footer" %} + © Copyright {{ date('Y') }} + {% endsection %} +
+ + +``` + +*path/home.phtml* +```html +{% extends "path/base/layout" %} + +{% title "Home page" %} + +{% section "main" %} +

Welcome !

+ {# @TODO integrate our new blog engine below ! #} +
+ This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! +
+{% endsection %} +``` + +**[HTML]** Would render as such : +```html + + + + + + Home page - AnExampleApp + + + +
+

Welcome !

+ +
+ This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! +
+
+
+ © Copyright 2022 +
+ + +``` + +### Inherit an already extended view + +We could use the previous file `path/home` and generate, let's say, the same page, but without a navigation menu. + +**[PICEA]** So, using this code: + +*path/home-navless.phtml* +```html +{% extends "path/home" %} + +{% section "header" %}{% endsection %} +``` + +**[HTML]** Would render as such : +```html + + + + + + Home page - AnExampleApp + + + +
+

Welcome !

+ +
+ This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! +
+
+
+ © Copyright 2022 +
+ + +``` + +Notice that the `
` tag is now empty, since we've redeclared it in our navless view. \ No newline at end of file diff --git a/docs/02-control-structure-function.md b/docs/02-control-structure-function.md new file mode 100644 index 0000000..bab4066 --- /dev/null +++ b/docs/02-control-structure-function.md @@ -0,0 +1,42 @@ +# Control structure - `function` + +Sometimes comes a need to have a small subset of code which is gonna be used twice (or more) +in the same view. Instead of simply duplicating the code (with everything that can then go wrong if you ever need +to play with it later), you could create a `function`. + +Functions are declared exactly like vanilla PHP. + +**[PICEA]** So, using this code: + +```html + +{% use Psr\Http\Message\ServerRequestInterface %} + +{% title "My generic title" %} + +{% function printCustomTitle(ServerRequestInterface $request) : bool %} + {% if $request->getAttribute('lean.route')->name === 'home' %} +

This is a custom title !

+ + {% return true %} + {% endif %} + + {% return false %} +{% endfunction %} + +{% if ! printCustomTitle($request) %} +

{{ title() }}

+{% endif %} +``` + +**[HTML]** Would yield: + +*page **is** 'home'* +```php +

This is a custom title !

+``` + +*page **is not** 'home'* +```php +

My generic title

+``` diff --git a/docs/02-control-structure-loops.md b/docs/02-control-structure-loops.md new file mode 100644 index 0000000..50f1f29 --- /dev/null +++ b/docs/02-control-structure-loops.md @@ -0,0 +1,193 @@ +# Control structure - Loops + +Picea's loop works the same as PHP original alternative syntax. + +There is, however, some minors improvments and a new `{% or %}` clause working much like the `{% else %}` clause in a comparison. + +## Loop using `for` / `or` / `continue` / `break` / `endfor` + +The simplest of sequence loop, `for` / `endfor` simply iterate over a given counter (or whatever your needs). + +**[PICEA]** So, using this code: + +```html +{% php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve' ], 0, random_int(0, 4)) %} + +

User list

+ +
    + {% for $i = 0; $i < count($users); $i++ %} + {% if $users[$i] === 'Steve' %} + {# Will leave the loop if user's name is Steve #} + {% break %} + {% else %} +
  • {{ $users[$i] }}
  • + {% endif %} + {% or %} +
  • Given user list was empty
  • + {% endfor %} +
+``` + +**[PHP]** Would compile like : + +```php + + +

User list

+ +
    + + + +
  • + +
  • Given user list was empty
  • +
+``` + +**[HTML]** And would render as such given random_int() returns a **3**: + +```html +

User list

+ +
    +
  • Tom
  • +
  • Sam
  • +
  • Mario
  • +
+``` + +**[HTML]** Or would render as such given random_int() returns a **0**: + +```html +

User list

+ +
    +
  • Tom
  • +
+``` + +## Loop using `foreach` / `or` / `continue` / `break` / `endforeach` + +The more complex `foreach` / `endforeach` allows to iterate over keys and values of an array. + +**[PICEA]** So, using this code: +```html +{# Generate a random list of 0 to 4 names #} +{% php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve', 'Joan' ], 0, random_int(0, 5)) %} + +

Random User list

+ +
    + {% foreach $users as $index => $name %} + {% if $name === 'Steve' %} + {# We skip Steve, but allows Joan #} + {% continue %} + {% endif %} +
  • {{ $name }}
  • + {% or %} +
  • Given user list was empty
  • + {% endforeach %} +
+``` + +**[HTML]** Could render as such if prior random_int() returns '**2**': +```html +

User list

+ +
    +
  • Tom
  • +
  • Sam
  • +
+``` + +**[HTML]** Could render as such given random_int() returns '**0**': +```html +

User list

+ +
    +``` + +## Loop using `while` / `or` / `continue` / `break` / `endwhile` + +This syntax allows to loop on a computed iteration count. + +**[PICEA]** So, using this code: + +```html +{% php $fruits = [ 'apple', 'pear', 'banana', 'tomato' ] %} + +

    Grocery list

    + +
      + {% while $item = array_pop($fruits) %} +
    • {{ ucfirst($item) }}
    • + {% or %} +
    • We should never see this, since the list is always populated !
    • + {% endwhile %} +
    +``` + +**[HTML]** Would render as such: + +```html +

    Grocery list

    + +
      +
    • Apple
    • +
    • Pear
    • +
    • Banana
    • +
    • Tomato
    • +
    +``` + +## Loop using `do` / `continue` / `break` / `while` + +This syntax do not have an alternative syntax in vanilla PHP. + +Picea allows for this unusual syntax to be used the same way you would with any other control structure. + +There is, however, no support for the `or` clause for this structure since it will iterate at least once. + +**[PICEA]** So, using this code: + +```html +

    Random number generator

    + +
      + {% do %} + {% php $number = random_int(1, 25); %} + + {% if $number === 10 %} + {# For a reason I'm not entirely sure why, 10 must not be displayed ! #} + {% continue %} + {% endif %} + +
    • {{ str_pad($number, 2, '0' ,STR_PAD_LEFT) }}
    • + {% while $number !== 15 %} {# Loop stops whenever $number is equal to 15 #} +
    +``` + +**[HTML]** Could render as such: + +```html +

    Random number generator

    + +
      +
    • 02
    • +
    • 12
    • +
    • 07
    • +
    • 13
    • +
    • 04
    • +
    • 07
    • +
    • 01
    • +
    • 16
    • +
    • 14
    • +
    • 24
    • +
    • 14
    • +
    • 01
    • +
    • 15
    • +
    +``` diff --git a/docs/02-control-structure-use.md b/docs/02-control-structure-use.md new file mode 100644 index 0000000..e9d7ef5 --- /dev/null +++ b/docs/02-control-structure-use.md @@ -0,0 +1,34 @@ +# Control structure - `Use` + +While working with inside a template, it might be needed that you reference some other namespaces. + +The use clause works exaclty like PHP's vanilla top of the page `use`. + +**[PICEA]** So, using this code: + +```html +{% use AnyVendor\My\Super\Randomizer %} + +{# One chance out of ten to end this loop %} +{% while Randomizer::run(1, 10) === 10 %} + {% if Randomizer::run(1, 100) < 50 %} + Lower than 50 ! + {% else %} + Greater than 50 ! + {% endif %} +{% endwhile %} + +

    DONE !

    +``` + +**[HTML]** Could render as such output: + +```html +Lower than 50 ! +Greater than 50 ! +Lower than 50 ! +Greater than 50 ! +Lower than 50 ! +Lower than 50 ! +

    DONE !

    +``` \ No newline at end of file diff --git a/docs/02-control-structure-view-include-block.md b/docs/02-control-structure-view-include-block.md new file mode 100644 index 0000000..a4be19c --- /dev/null +++ b/docs/02-control-structure-view-include-block.md @@ -0,0 +1,112 @@ +# Control structure - View (or `include`) / Block + +There is three ways to include other views into your current view `{% view %} / {% include %}` or +using `{% block %}{% endblock %}`. + +The order of loading depends on the order given within your directory configuration; the first file found from it +will be used. + +## Inline `view` (string $path, array|null $arguments = null) + +You must provide a valid `$path` from which the template will be loaded. If no `$arguments` are provided, defined +variables from the caller's scope will be given (from `get_defined_vars()`). + +**[PICEA]** So, using this code: + +*path/base/nav.phtml* +```html + +``` + +*path/base/nav-item.phtml* +```html +{{ $item->name }}" +``` + +**[HTML]** Could render such as : +```html + +``` + +## `include` (string $path) content from external file + +Whenever you need to `include` a raw file from one of your view directories + +## Reusable `block` (string $path, ...$arguments) / `endblock` + +A better way to achieve this behaviour could be to create a `block` which you can define as needed. + +**[PICEA]** So, using this code: + +*path/base/nav.phtml* +```html + +``` + +*path/base/nav-item-block.phtml* +```html +{% arguments string $name, string $anchor, int $index = 0 %} + +{{ $name }}" +``` + +**[HTML]** Would render the same as the `view` example : +```html + +``` + +### Extending a `block` using `define` and `slot` + +You might need to define some custom content inside of a `block`. + +You can do so by using `define` and `slot`. + +**[PICEA]** So, using this code: + +*path/base/nav.phtml* +```html + +``` + +*path/base/nav-item-block.phtml* +```html +{% arguments string $name, string $anchor, int $index = 0 %} + +{% define slot %} + +{{ $name }}" +``` + +**[HTML]** Would render the same as the `view` example : +```html + +``` \ No newline at end of file diff --git a/docs/10-extension-json.md b/docs/10-extension-json.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/10-extension-language.md b/docs/10-extension-language.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/10-extension-money.md b/docs/10-extension-money.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/10-extension-php.md b/docs/10-extension-php.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/10-extension-title.md b/docs/10-extension-title.md new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/docs/10-extension-title.md @@ -0,0 +1 @@ + function($c) { + return new Picea($c->get(Context::class), $c->get(Cache::class), $c->get(Compiler::class), null, $c->get(FileFetcher::class), null, getenv("DEBUG")); + }, + + Context::class => function($c) { + return new BaseContext( $c->get(Lean\Lean::class)->getPiceaContext() ); + }, + + Compiler::class => function($c) { + return new Compiler(new class(array_merge([ + $c->get(LanguageExtension::class), + $c->get(TitleExtension::class), + $c->get(MoneyExtension::class), + $c->get(UrlExtension::class), + $c->get(Method\Form::class), + $c->get(Method\Pagination::class), + $c->get(Request::class), + ], class_exists(\Taxus\Picea\Extension::class) ? [ $c->get(\Taxus\Picea\Extension::class) ] : [], + array_map(fn($class) => $c->get($class), $c->get(Lean\Lean::class)->getPiceaExtensions() ))) extends DefaultRegistrations { + + public function registerAll(Compiler $compiler) : void + { + parent::registerAll($compiler); + ( new Ui() )->registerFormExtension($compiler); + } + + }); + }, + + Request::class => autowire(Request::class), + + Method\Form::class => autowire(Method\Form::class), + + Method\Pagination::class => autowire(Method\Pagination::class), + + LanguageExtension::class => create(LanguageExtension::class)->constructor(get(LanguageHandler::class)), + + LanguageHandler::class => function($c) { + return new class( $c->get(Tell\I18n::class) ) implements LanguageHandler { + public Tell\I18n $tell; + + public function __construct(Tell\I18n $tell) { + $this->tell = $tell; + } + + public function languageFromKey(string $key, array $variables = []) : array|string + { + return $this->tell->fromKey($key, $variables) ?: ""; + } + }; + }, + + TitleExtension::class => autowire(TitleExtension::class), + + MoneyExtension::class => autowire(MoneyExtension::class), + + UrlExtension::class => create(UrlExtension::class)->constructor(get(Context::class), getenv("URL_BASE"), get('git.commit')), + + Cache::class => create(Opcache::class)->constructor(getenv("CACHE_PATH"), get(Context::class)), + + FileFetcher::class => function($c) { + return new FileFetcher($c->get(Lean\Lean::class)->getViewPaths()); + }, +]; +``` + diff --git a/src/Builder/ClassTemplate.php b/src/Builder/ClassTemplate.php index 5af29a0..017cdb9 100644 --- a/src/Builder/ClassTemplate.php +++ b/src/Builder/ClassTemplate.php @@ -85,6 +85,14 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { public static function getSourceLineFromException(int $sourceLine) : ? int { + $selfSource = file_get_contents(__FILE__); + + foreach(explode("\n", $selfSource) as $line => $content) { + if ( strpos($content, str_replace('$', '%', '/*$EXCEPTION_LINE_BASE$*/')) !== false ) { + return $sourceLine - $line; + } + } + $sourceFile = file_get_contents("%TEMPLATE%"); if ( $sourceFile ) { diff --git a/src/Compiler/Context.php b/src/Compiler/Context.php index 0cbf307..b490c40 100644 --- a/src/Compiler/Context.php +++ b/src/Compiler/Context.php @@ -24,6 +24,8 @@ abstract class Context { public array $useStack = []; + public int $functions = 0; + public array $functionStack = []; public array $hooks = []; diff --git a/src/ControlStructure/BlockToken.php b/src/ControlStructure/BlockToken.php index 6fe1580..ed581e8 100644 --- a/src/ControlStructure/BlockToken.php +++ b/src/ControlStructure/BlockToken.php @@ -33,7 +33,7 @@ class BlockToken implements ControlStructure { sprintf('A block awaiting arguments `%s` instead received `%s` with values `%s`', '$arguments', implode(', ', array_map('gettype', \$inlineVariables ?? [])), json_encode(\$inlineVariables)) ); } - ?> + /*%EXCEPTION_LINE_BASE%*/?> PHP; case "define": diff --git a/src/ControlStructure/EchoToken.php b/src/ControlStructure/EchoToken.php new file mode 100644 index 0000000..f20c841 --- /dev/null +++ b/src/ControlStructure/EchoToken.php @@ -0,0 +1,28 @@ +"; + + case "echo.raw": + return ""; + } + } + + public static function echoRaw($arguments) : string + { + return ""; + } + + public static function echoSafe($arguments) : string + { + return ""; + } +} diff --git a/src/ControlStructure/EndRawToken.php b/src/ControlStructure/EndRawToken.php deleted file mode 100644 index e3d6907..0000000 --- a/src/ControlStructure/EndRawToken.php +++ /dev/null @@ -1,12 +0,0 @@ -functions++; + return $this->printFunction($context, $arguments); case "return": @@ -31,7 +33,6 @@ class FunctionToken implements ControlStructure { protected function printFunction($context, ?string $arguments) : string { - $context->functions++; return ""; } diff --git a/src/ControlStructure/IncludeToken.php b/src/ControlStructure/IncludeToken.php index 07970f5..2fd91e0 100644 --- a/src/ControlStructure/IncludeToken.php +++ b/src/ControlStructure/IncludeToken.php @@ -6,7 +6,7 @@ class IncludeToken implements ControlStructure { public string $token = "include"; - public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { - return "picea->inlineContent($arguments); ?>"; + public function parse(\Picea\Compiler\Context &$context, ? string $viewPath, string $token) { + return "picea->inlineContent($viewPath); ?>"; } } diff --git a/src/ControlStructure/RawToken.php b/src/ControlStructure/RawToken.php deleted file mode 100644 index 4d4faba..0000000 --- a/src/ControlStructure/RawToken.php +++ /dev/null @@ -1,12 +0,0 @@ -picea = $picea; + + $this->defineError($previous, $compiledObject); + } + + protected function defineError(\Throwable $previous, object $compiledObject) : void + { + $loadedTemplates = array_flip($this->picea->loadedTemplateFile); + + foreach($previous->getTrace() as $trace) { + if ( isset($trace['file'], $loadedTemplates[$trace['file']]) ) { + $class = $loadedTemplates[ $trace['file'] ]; + + $content = include($trace['file']); + + $this->file = $content['view']; + $this->line = $class::getSourceLineFromException($trace['line']); + + return; + } + } + } + + protected function getTemplateFile(string $filePath) : ? array + { + $content = null; + + if ( is_array($content) && isset($content['classname'], $content['namespace'], $content['view'], $content['extends']) ) { + return $content; + } + + return null; + } +} diff --git a/src/Exception/RenderingError.php b/src/Exception/RenderingError.php deleted file mode 100644 index a31acca..0000000 --- a/src/Exception/RenderingError.php +++ /dev/null @@ -1,129 +0,0 @@ - - */ -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; - } - } - } - } - } -} diff --git a/src/Extension/PrintExtension.php b/src/Extension/PrintExtension.php index 0589ab6..a9baf2e 100644 --- a/src/Extension/PrintExtension.php +++ b/src/Extension/PrintExtension.php @@ -6,7 +6,7 @@ use Picea\Compiler\Context; class PrintExtension implements Extension { - public array $token = [ "print", "print.safe", "print.raw" ]; + public array $token = [ "echo", "echo.raw", "print", "print.raw", "printf", "printf.raw" ]; public int $flag = \ENT_QUOTES; @@ -21,12 +21,19 @@ class PrintExtension implements Extension { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string { switch($token) { + case 'echo': case 'print': - case "print.safe": - return "flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; + return "flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; + case 'echo.raw': case "print.raw": - return ""; + return ""; + + case 'printf': + return "flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; + + case 'printf.raw': + return ""; } } diff --git a/src/Language/DefaultRegistrations.php b/src/Language/DefaultRegistrations.php index 2742f0e..3719ad7 100644 --- a/src/Language/DefaultRegistrations.php +++ b/src/Language/DefaultRegistrations.php @@ -49,6 +49,7 @@ class DefaultRegistrations implements LanguageRegistration $compiler->registerControlStructure(new \Picea\ControlStructure\SwitchToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\DefaultToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\BreakToken()); + $compiler->registerControlStructure(new \Picea\ControlStructure\ContinueToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ExtendsToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\SectionToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\FunctionToken()); diff --git a/src/Picea.php b/src/Picea.php index 3542036..5801a1d 100644 --- a/src/Picea.php +++ b/src/Picea.php @@ -20,8 +20,6 @@ class Picea implements LanguageRegistration public string $builderTemplatePath; - public Closure $responseHtml; - public Caching\Cache $cache; public FileFetcher $fileFetcher; @@ -35,7 +33,6 @@ class Picea implements LanguageRegistration public array $compiled = []; public function __construct( - ? Closure $responseHtml = null, ? Compiler\Context $context = null, ? Caching\Cache $cache = null, ? Compiler $compiler = null, @@ -44,7 +41,6 @@ class Picea implements LanguageRegistration ? string $builderTemplatePath = null, bool $debug = false ){ - $this->response = $responseHtml; $this->cache = $cache ?? new Caching\Memory(""); $this->context = $context ?? new Compiler\BaseContext(); $this->languageRegistration = $languageRegistration ?? new Language\DefaultRegistrations(); @@ -75,53 +71,15 @@ class Picea implements LanguageRegistration return call_user_func($object); } catch(\Throwable $ex) { - #throw $ex; - # Temporary class for an experiment on retrieving more accurate debug info - throw new class($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex) extends \Exception { - - protected Picea $picea; - - public function __construct(object $compiledObject, Picea $picea, string $message, int $code, \Throwable $previous) - { - parent::__construct($message, $code, $previous); - - $this->picea = $picea; - - # $template = $this->getTemplateFile( $previous->getFile() ); - $this->defineError($previous, $compiledObject); - } - - protected function defineError(\Throwable $previous, object $compiledObject) : void - { - $loadedTemplates = array_flip($this->picea->loadedTemplateFile); - - foreach($previous->getTrace() as $trace) { - if ( isset($trace['file'], $loadedTemplates[$trace['file']]) ) { - $class = $loadedTemplates[ $trace['file'] ]; - - $content = include($trace['file']); - - $this->file = $content['view']; - $this->line = $class::getSourceLineFromException($trace['line']); - - return; - } - } - } - - protected function getTemplateFile(string $filePath) : ? array - { - $content = null; - - if ( is_array($content) && isset($content['classname'], $content['namespace'], $content['view'], $content['extends']) ) { - return $content; - } - - return null; - } - - }; + if (! $ex instanceof Exception\RenderHtmlException ) { + throw new Exception\RenderHtmlException($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex); + } + else { + throw $ex; + } } + + exit(); } /**