- Finalizing the documentation before tagging 1.0 release

This commit is contained in:
Dave M. 2022-12-21 04:20:11 +00:00
parent bb29a56077
commit 46535c2734
29 changed files with 933 additions and 211 deletions

47
docs/00-intro.md Normal file
View File

@ -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
<!DOCTYPE html>
<html>
<head>
<title>Picea's simple </title>
</head>
<body>
<nav>
{% foreach $navigation as $navItem %}
<a href="{% route $navItem->route %}" title="{{ $navItem->description }}">
{{ $navItem->caption }}
</a>
{% endforeach %}
</nav>
{{ $someText }}
</body>
</html>
```

26
docs/01-comment.md Normal file
View File

@ -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 #}
<div>
{# Is it hello or good bye ? not sure on this ! #}
<i>Good bye world !</i>
</div>
{# This is a multi-line comment
<div>This part will not be rendered</div>
#}
```
**[HTML]** Would render as such:
```html
<div>
<i>Good bye world !</i>
</div>
```

73
docs/01-echoing.md Normal file
View File

@ -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 <safe> content" %}
{% print "This is the third way to output <safe> content" %}
<hr>
{% echo.raw $someHTML %}
{% print.raw $someOtherHTML %}
{{= $someMoreHTML }}
```
**[PHP]** Would yield internally:
```php
<?php echo htmlspecialchars((string) "Hello World !", 3, 'UTF-8', true) ?>
<?php echo htmlspecialchars((string) "This is another way to output &lt;safe&gt; content", 3, 'UTF-8', true) ?>
<?php echo htmlspecialchars((string) "This is another way to output &lt;safe&gt; content", 3, 'UTF-8', true) ?>
<hr>
<?php echo $someHTML ?>
<?php echo $someOtherHTML ?>
<?php echo $someMoreHTML ?>
```
## 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';
%}
<span>{% printf 'There are %d monkeys in the %s', $num, $location %}</span>
<i>{% printf.raw 'There are %d monkeys in the %s', $num, $location %}</i>
```
**[PHP]** Would render internally as :
```php
<?php
$num = 5;
$location = 'tree';
?>
<span><?php echo htmlspecialchars((string) sprintf('There are %d monkeys in the %s', $num, location), 3, 'UTF-8', true); ?></span>
<i><?php printf('There are %d monkeys in the %s', $num, location) ?></i>
```

View File

@ -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 %}
<span>Found 'beer' into {{ $search }} !</span>
{% elseif strpos($search, 'liquor') !== false %}
<span>Found 'liquor' into {{ $search }} !</span>
{% else %}
<b>Neither 'beer' or 'liquor' were found in {{ $search }}</b>
{% endif %}
```
**[PHP]** Would yield:
```php
<?php $search = 'Pack my box with five dozen liquor jugs'; ?>
<?php if (strpos($search, 'beer') !== false): ?>
<span>Found 'beer' into <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?> !</span>
<?php elseif (strpos($search, 'beer') !== false): ?>
<span>Found 'liquor' into <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?> !</span>
<?php else: ?>
<b>Neither 'beer' or 'liquor' were found in <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?></b>
<?php endif ?>
```
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
<?php $selected = random_int(0,5) ?>
<?php switch($selected):
case 1: ?>
<?php echo htmlspecialchars((string) "One person selected", 3, 'UTF-8', true) ?>
<?php break; ?>
<?php case 2: ?>
<?php default: ?>
<?php echo htmlspecialchars((string) "Multiple person ($selected) selected", 3, 'UTF-8', true) ?>
<?php break; ?>
<?php endswitch; ?>
```

View File

@ -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
<!DOCTYPE html>
<html>
<head>
{% section "head" %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title() }} - AnExampleApp</title>
{% section %}
</head>
<body>
<header id="header"">
{% section "header" %}{% view "path/base/navigation" %}{% endsection %}
</header>
<main id="main">{% section "main" %}{% endsection %}</main>
<footer id="footer">
{% section "footer" %}
&copy; Copyright {{ date('Y') }}
{% endsection %}
</footer>
</body>
</html>
```
*path/home.phtml*
```html
{% extends "path/base/layout" %}
{% title "Home page" %}
{% section "main" %}
<h1>Welcome !</h1>
{# @TODO integrate our new blog engine below ! #}
<article>
This is our new blog ! We hope you are gonna enjoy your stay on our new platform !
</article>
{% endsection %}
```
**[HTML]** Would render as such :
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home page - AnExampleApp</title>
</head>
<body>
<header id="header"">
<nav>
<a href="#home">Home</a>
<a href="#login">Login</a>
</nav>
</header>
<main id="main">
<h1>Welcome !</h1>
<article>
This is our new blog ! We hope you are gonna enjoy your stay on our new platform !
</article>
</main>
<footer id="footer">
&copy; Copyright 2022
</footer>
</body>
</html>
```
### 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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Home page - AnExampleApp</title>
</head>
<body>
<header id="header""></header>
<main id="main">
<h1>Welcome !</h1>
<article>
This is our new blog ! We hope you are gonna enjoy your stay on our new platform !
</article>
</main>
<footer id="footer">
&copy; Copyright 2022
</footer>
</body>
</html>
```
Notice that the `<header>` tag is now empty, since we've redeclared it in our navless view.

View File

@ -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' %}
<h4 class="title">This is a custom title !</h4>
{% return true %}
{% endif %}
{% return false %}
{% endfunction %}
{% if ! printCustomTitle($request) %}
<h1>{{ title() }}</h1>
{% endif %}
```
**[HTML]** Would yield:
*page **is** 'home'*
```php
<h4>This is a custom title !</h4>
```
*page **is not** 'home'*
```php
<h4>My generic title</h4>
```

View File

@ -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)) %}
<h1>User list</h1>
<ul>
{% for $i = 0; $i < count($users); $i++ %}
{% if $users[$i] === 'Steve' %}
{# Will leave the loop if user's name is Steve #}
{% break %}
{% else %}
<li>{{ $users[$i] }}</li>
{% endif %}
{% or %}
<li class="empty">Given user list was empty</li>
{% endfor %}
</ul>
```
**[PHP]** Would compile like :
```php
<?php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve' ], 0, random_int(0, 4)) ?>
<h1>User list</h1>
<ul>
<?php for($i = 0; $i < count($users); $i++): ?><?php $for_a1kro2k2o = true;
<?php if ($users[$i] === 'Steve'): ?>
<?php break ?>
<?php else: ?>
<li><?php echo htmlspecialchars((string) $users[$i], 3, 'UTF-8', true) ?></li>
<?php endif ?>
<?php endfor ?><?php if (empty($for_a1kro2k2o)): ?><li class="empty">Given user list was empty</li><?php endif ?>
</ul>
```
**[HTML]** And would render as such given random_int() returns a **3**:
```html
<h1>User list</h1>
<ul>
<li>Tom</li>
<li>Sam</li>
<li>Mario</li>
</ul>
```
**[HTML]** Or would render as such given random_int() returns a **0**:
```html
<h1>User list</h1>
<ul>
<li class="empty">Tom</li>
</ul>
```
## 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)) %}
<h1>Random User list</h1>
<ul>
{% foreach $users as $index => $name %}
{% if $name === 'Steve' %}
{# We skip Steve, but allows Joan #}
{% continue %}
{% endif %}
<li>{{ $name }}</li>
{% or %}
<li class="empty">Given user list was empty</li>
{% endforeach %}
</ul>
```
**[HTML]** Could render as such if prior random_int() returns '**2**':
```html
<h1>User list</h1>
<ul>
<li>Tom</li>
<li>Sam</li>
</ul>
```
**[HTML]** Could render as such given random_int() returns '**0**':
```html
<h1>User list</h1>
<ul></ul>
```
## 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' ] %}
<h1>Grocery list</h1>
<ul>
{% while $item = array_pop($fruits) %}
<li>{{ ucfirst($item) }}</li>
{% or %}
<li class="empty">We should never see this, since the list is always populated !</li>
{% endwhile %}
</ul>
```
**[HTML]** Would render as such:
```html
<h1>Grocery list</h1>
<ul>
<li>Apple</li>
<li>Pear</li>
<li>Banana</li>
<li>Tomato</li>
</ul>
```
## 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
<h1>Random number generator</h1>
<ul>
{% 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 %}
<li>{{ str_pad($number, 2, '0' ,STR_PAD_LEFT) }}</li>
{% while $number !== 15 %} {# Loop stops whenever $number is equal to 15 #}
</ul>
```
**[HTML]** Could render as such:
```html
<h1>Random number generator</h1>
<ul>
<li>02</li>
<li>12</li>
<li>07</li>
<li>13</li>
<li>04</li>
<li>07</li>
<li>01</li>
<li>16</li>
<li>14</li>
<li>24</li>
<li>14</li>
<li>01</li>
<li>15</li>
</ul>
```

View File

@ -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 %}
<span>Lower than 50 !</span>
{% else %}
<strong>Greater than 50 !</strong>
{% endif %}
{% endwhile %}
<h4>DONE !</h4>
```
**[HTML]** Could render as such output:
```html
<span>Lower than 50 !</span>
<span>Greater than 50 !</span>
<span>Lower than 50 !</span>
<span>Greater than 50 !</span>
<span>Lower than 50 !</span>
<span>Lower than 50 !</span>
<h4>DONE !</h4>
```

View File

@ -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
<nav>
{% foreach $navigation as $index => $navItem %}
{% view "path/base/nav-item", [ 'item' => $navItem, 'index' => $index] %}
{% endforeach %}
</nav>
```
*path/base/nav-item.phtml*
```html
<a href="{{ $item->anchor }}" tabindex="{{ $index }}">{{ $item->name }}"</a>
```
**[HTML]** Could render such as :
```html
<nav>
<a href="#top">Top</a>
<a href="#bottom">Bottom</a>
<a href="#left">Left</a>
<a href="#right">Right</a>
</nav>
```
## `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
<nav>
{% foreach $navigation as $index => $navItem %}
{% block "path/base/nav-item-block", $navItem->name, $navItem->anchor, $index %}{% endblock %}
{% endforeach %}
</nav>
```
*path/base/nav-item-block.phtml*
```html
{% arguments string $name, string $anchor, int $index = 0 %}
<a href="{{ $anchor }}" tabindex="{{ $index }}">{{ $name }}"</a>
```
**[HTML]** Would render the same as the `view` example :
```html
<nav>
<a href="#top">Top</a>
<a href="#bottom">Bottom</a>
<a href="#left">Left</a>
<a href="#right">Right</a>
</nav>
```
### 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
<nav>
{% foreach $navigation as $index => $navItem %}
{% block "path/base/nav-item-block", $navItem->name, $navItem->anchor, $index %}
{% slot "attributes" %} class="nav-item" href="{{ $anchor }}" {% endslot %}
{% endblock %}
{% endforeach %}
</nav>
```
*path/base/nav-item-block.phtml*
```html
{% arguments string $name, string $anchor, int $index = 0 %}
{% define slot %}
<a {% slot "attributes" %}href="{{ $anchor }}" tabindex="{{ $index }}"{% endslot %}>{{ $name }}"</a>
```
**[HTML]** Would render the same as the `view` example :
```html
<nav>
<a href="#top">Top</a>
<a href="#bottom">Bottom</a>
<a href="#left">Left</a>
<a href="#right">Right</a>
</nav>
```

View File

View File

View File

0
docs/10-extension-php.md Normal file
View File

View File

@ -0,0 +1 @@
<?php

0
docs/10-extension-url.md Normal file
View File

View File

@ -0,0 +1,82 @@
# Using Dependency Injection
Here's an excerpt from Lean's definition file 'template.php' which also loads Picea\Ui.
It also insert `Tell` inside the LanguageHandler which handles the `{% _ 'relative.lang.key' %}` and `{% lang 'my.full.lang.key' %}`.
```php
use function DI\autowire, DI\create, DI\get;
use Zend\Diactoros\Response\HtmlResponse;
use Picea\{ Picea, Caching\Cache, Caching\Opcache, Compiler, Compiler\Context, Compiler\BaseContext, FileFetcher, Language\DefaultRegistrations, Method\Request };
use Picea\Extension\{ LanguageHandler, LanguageExtension, TitleExtension, MoneyExtension, UrlExtension };
use Picea\Ui\{ Method, Ui };
return [
Picea::class => 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());
},
];
```

View File

@ -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 ) {

View File

@ -24,6 +24,8 @@ abstract class Context {
public array $useStack = [];
public int $functions = 0;
public array $functionStack = [];
public array $hooks = [];

View File

@ -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":

View File

@ -0,0 +1,28 @@
<?php
namespace Picea\ControlStructure;
class EchoToken implements ControlStructure {
public array $token = [ "echo", "echo.raw" ];
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) {
case "echo":
return "<?php \\Picea\\ControlStructure\\EchoToken::echoSafe($arguments) ?>";
case "echo.raw":
return "<?php \\Picea\\ControlStructure\\EchoToken::echoRaw($arguments) ?>";
}
}
public static function echoRaw($arguments) : string
{
return "<?php echo $arguments ?>";
}
public static function echoSafe($arguments) : string
{
return "<?php echo $arguments ?>";
}
}

View File

@ -1,12 +0,0 @@
<?php
namespace Picea\ControlStructure;
class EndRawToken implements ControlStructure {
public string $token = "endraw";
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
}
}

View File

@ -9,6 +9,8 @@ class FunctionToken implements ControlStructure {
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) {
case "function":
$context->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 "<?php function $arguments { ?>";
}

View File

@ -6,7 +6,7 @@ class IncludeToken implements ControlStructure {
public string $token = "include";
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php echo \$___class__template->picea->inlineContent($arguments); ?>";
public function parse(\Picea\Compiler\Context &$context, ? string $viewPath, string $token) {
return "<?php echo \$___class__template->picea->inlineContent($viewPath); ?>";
}
}

View File

@ -1,12 +0,0 @@
<?php
namespace Picea\ControlStructure;
class RawToken implements ControlStructure {
public string $token = "raw";
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Picea\Exception;
use Picea\Picea;
/**
* Whenever an error occured rendering a view, this is the exception that will show up.
*
*/
class RenderHtmlException 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;
$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;
}
}

View File

@ -1,129 +0,0 @@
<?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

@ -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 "<?php echo htmlspecialchars(sprintf($arguments), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
return "<?php echo htmlspecialchars((string) $arguments, {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
case 'echo.raw':
case "print.raw":
return "<?php echo sprintf($arguments) ?>";
return "<?php echo $arguments ?>";
case 'printf':
return "<?php echo htmlspecialchars(sprintf((string) $arguments), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
case 'printf.raw':
return "<?php printf((string) $arguments) ?>";
}
}

View File

@ -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());

View File

@ -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();
}
/**