12 Commits
68 changed files with 1389 additions and 374 deletions

No files matched your search

+47
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
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
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:
```html
<?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 :
```html
<?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>
```
+86
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; ?>
```
@@ -0,0 +1,170 @@
# 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.
## Overview of `section` (string $name, array $options)
In your `$name` variable, accepted characters are alpha-numeric and those specific caracters : `.-_:`, so
names suches as :
`body.content`
`my-cool-section:heading`
`MyOtherSection`
`_another_accepted_name`
Allowed options are :
string `action` : allowed are `prepend`, `default`, `append`
int `order` : if you must
### Actions can also be passed using specials tokens :
`section.prepend` and `section.append` can also be used without passing an `action` option.
*path/home-nav.phtml*
```html
{% extends "path/home" %}
{% section "header" %}
<a href="#mylink">First link here !</a>
{% endsection %}
```
*path/home-admin-nav.phtml*
```html
{% extends "path/home-nav" %}
{% section.prepend "header" %}
<h4>My new NAV header</h4>
{% endsection %}
{% section.append "header" %}
<a href="#mylink">Second link here !</a>
{% endsection %}
```
**[HTML]** Would render as such :
```html
<h4>My new NAV header</h4>
<a href="#mylink">First link here !</a>
<a href="#mylink">Second link here !</a>
```
+42
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>
```
+193
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>
```
+34
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>
```
@@ -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
Whitespace-only changes.
+4
View File
@@ -0,0 +1,4 @@
# Extension - Creating an extension
+4
View File
@@ -0,0 +1,4 @@
# Extension - `json`, `json.pretty`, `json.html`
This extension
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
<?php
View File
Whitespace-only changes.
+82
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 Laminas\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());
},
];
```
+27 -3
View File
@@ -8,10 +8,13 @@ namespace %NAMESPACE%;
if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
class %CLASSNAME% %EXTENDS% { class %CLASSNAME% %EXTENDS% {
public array $blockList = []; public array $blockList = [];
public array $sectionList = []; public array $sectionList = [];
public array $sectionStack = [];
public array $variableList = []; public array $variableList = [];
public ?object $thisProxy = null; public ?object $thisProxy = null;
@@ -24,6 +27,8 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
public bool $renderingInsideSection = false; public bool $renderingInsideSection = false;
public int $depth = 0;
public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) { public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) {
$this->picea = $picea; $this->picea = $picea;
$this->variableList = $variablesList; $this->variableList = $variablesList;
@@ -37,15 +42,26 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
public function output(array $variablesList = []) : void public function output(array $variablesList = []) : void
{ {
$__event = new \Picea\Builder\ClassTemplateEvent();
$this->rendering = true; $this->rendering = true;
( function($___class__template, $___global_variables, $___variables, $picea) { $this->depth++;
$__event->eventExecute(\Picea\Event\Builder\ClassTemplateOutputing::class, $variablesList);
( function($___class__template, $___global_variables, $___variables, $__event, $picea) {
extract($___global_variables); extract($___global_variables);
extract($___variables, \EXTR_OVERWRITE); extract($___variables, \EXTR_OVERWRITE);
?>%CONTENT%<?php ?>%CONTENT%<?php
} )->call($this->thisProxy ?? new class(){}, $this, $this->variableList, $variablesList, $this->picea); } )->call($this->thisProxy ?? new class() {}, $this, $this->variableList, $variablesList, $__event, $this->picea);
$__event->eventExecute(\Picea\Event\Builder\ClassTemplateOutputDone::class, $variablesList);
%PARENT_OUTPUT% %PARENT_OUTPUT%
$this->depth--;
$this->rendering = false; $this->rendering = false;
} }
@@ -54,7 +70,7 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
$result = []; $result = [];
foreach([ 'prepend', 'default', 'append' ] as $item) { foreach([ 'prepend', 'default', 'append' ] as $item) {
if ( ! is_array($this->sectionList[$name][$item]) ) continue; if ( !isset($this->sectionList[$name][$item]) || ! is_array($this->sectionList[$name][$item]) ) continue;
usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']); usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']);
@@ -85,6 +101,14 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) {
public static function getSourceLineFromException(int $sourceLine) : ? int 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%"); $sourceFile = file_get_contents("%TEMPLATE%");
if ( $sourceFile ) { if ( $sourceFile ) {
+8
View File
@@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Picea\Builder;
class ClassTemplateEvent
{
use \Picea\EventTrait;
}
+27 -10
View File
@@ -2,6 +2,7 @@
namespace Picea; namespace Picea;
use Picea\Exception\RegisterExtensionToken;
use Picea\Extension\FunctionExtension; use Picea\Extension\FunctionExtension;
use Picea\Language\LanguageRegistration; use Picea\Language\LanguageRegistration;
@@ -47,18 +48,19 @@ class Compiler
$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 = strtolower(trim($token)); $token = strtolower(trim($token));
$tokenName = explode('.', $token, 2)[0];
# @TODO Refractor this parts to allows registration to the tag's name # @TODO Refractor this parts to allows registration to the tag's name
if ( $this->tagList[$token] ?? false ) { if ( $this->tagList[$tokenName] ?? false ) {
return $this->tagList[$token]->parse($context, $arguments, $token); return $this->tagList[$tokenName]->parse($context, $arguments, $token);
} }
elseif ( $this->extensionList[$token] ?? false ) { elseif ( $this->extensionList[$tokenName] ?? false ) {
return $this->extensionList[$token]->parse($context, $arguments, $token); return $this->extensionList[$tokenName]->parse($context, $arguments, $token);
} }
else { else {
throw new \LogicException("Impossible to find token `$token` declared in `{$matches[2]}`. Perhapse you forgot to add a custom token to Picea's engine ?"); throw new \LogicException("Impossible to find token `$tokenName` declared in `{$matches[2]}`. Perhapse you forgot to add a custom token to Picea's engine ?");
} }
}, $this->sourceCode); }, $this->sourceCode);
@@ -87,6 +89,10 @@ class Compiler
public function registerControlStructure(ControlStructure\ControlStructure $controlStructureObject) : self public function registerControlStructure(ControlStructure\ControlStructure $controlStructureObject) : self
{ {
foreach($controlStructureObject->tokens ?? (array) ( $controlStructureObject->token ?? [] ) as $token) { foreach($controlStructureObject->tokens ?? (array) ( $controlStructureObject->token ?? [] ) as $token) {
if ( strpos($token, '.') !== false ) {
throw new RegisterExtensionToken(sprintf("Could not register token '%s' from control structure '%s' in this version. Options are now delt by extensions themselve instead.", $token, $controlStructureObject::class));
}
$this->tagList[strtolower($token)] = $controlStructureObject; $this->tagList[strtolower($token)] = $controlStructureObject;
} }
@@ -98,6 +104,10 @@ class Compiler
$tokens = $extension->tokens ?? (array) ( $extension->token ?? [] ); $tokens = $extension->tokens ?? (array) ( $extension->token ?? [] );
foreach($tokens as $token) { foreach($tokens as $token) {
if ( strpos($token, '.') !== false ) {
throw new RegisterExtensionToken(sprintf("Could not register token '%s' from extension '%s' in this version. Options are now delt by extensions themselve instead.", $token, $extension::class));
}
$this->extensionList[strtolower($token)] = $extension; $this->extensionList[strtolower($token)] = $extension;
} }
@@ -122,15 +132,22 @@ class Compiler
} }
public function __toString() : string
{
return "WHATAFAK";
}
public function getExtensionFromToken(string $name) : Extension\Extension public function getExtensionFromToken(string $name) : Extension\Extension
{ {
if ( false === $this->extensionList[$name] ?? false ) { $tokenName = explode('.', $name)[0];
if ( ! isset($this->extensionList[$tokenName]) ) {
throw new \InvalidArgumentException(<<<MSG throw new \InvalidArgumentException(<<<MSG
Requested extension from token `$name` could not be found from loaded Picea compiler's extension. Requested extension from token `$tokenName` could not be found from loaded Picea compiler's extension.
MSG); MSG);
} }
return $this->extensionList[$name]; return $this->extensionList[$tokenName];
} }
public function exportFunctions() : void public function exportFunctions() : void
+12
View File
@@ -14,6 +14,8 @@ abstract class Context {
public string $compiledSource = ""; public string $compiledSource = "";
public ? string $sectionAction = null;
public string $viewPath = ""; public string $viewPath = "";
public string $filePath = ""; public string $filePath = "";
@@ -24,6 +26,8 @@ abstract class Context {
public array $useStack = []; public array $useStack = [];
public int $functions = 0;
public array $functionStack = []; public array $functionStack = [];
public array $hooks = []; public array $hooks = [];
@@ -84,6 +88,14 @@ abstract class Context {
} }
public function tokenOptions(string $token, bool $export = false) : array|string
{
$options = explode('.', strtolower($token));
array_shift($options);
return $export ? var_export($options, true) : $options;
}
public function pushFunction($name, Callable $callable) : void public function pushFunction($name, Callable $callable) : void
{ {
$this->functionStack[$name] = $callable; $this->functionStack[$name] = $callable;
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Picea\ControlStructure;
abstract class AbstractLoop implements ControlStructure {
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) {
case "while":
case "foreach":
$name = "$".uniqid("{$token}_");
$stack = array_filter($context->iterationStack ?? [], function($item) {
return ! $item['or'];
});
$count = count($stack);
if ( $count > 0 ) {
$name .= "[" . end($stack)['uid'] . "]";
}
$context->iterationStack[] = [
'or' => false,
'uid' => $name,
'token' => "end{$token}",
];
return "<?php $token ($arguments): $name = ( $name ?? 0 ) + 1; ?>";
case "endwhile":
case "endforeach":
$last = end($context->iterationStack);
if ( $last['or'] === false ) {
$output = "<?php $token; ?>";
}
else {
$output = "<?php endif; if ( isset({$last['uid']}) ) unset({$last['uid']}); ?>";
}
array_pop($context->iterationStack);
return $output;
}
}
}
+7 -5
View File
@@ -6,7 +6,7 @@ class BlockToken implements ControlStructure {
public array $token = [ "arguments", "block", "endblock", "define", "slot", "endslot", "using" ]; public array $token = [ "arguments", "block", "endblock", "define", "slot", "endslot", "using" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
static $slotDefinitions = []; static $slotDefinitions = [];
switch($token) { switch($token) {
@@ -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)) 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; PHP;
case "define": case "define":
@@ -49,7 +49,9 @@ class BlockToken implements ControlStructure {
$def = end($slotDefinitions); $def = end($slotDefinitions);
list($name, $definition) = array_pad(explode(',', $arguments, 2), 2, ""); list($name, $definition) = array_pad(explode(',', $arguments, 2), 2, "");
$loops = count($context->iterationStack ?? []) ? ",". implode(', ', array_filter(array_column($context->iterationStack, 'uid'), fn($e) => strpos($e, '[') === false)) : null;
if ($def->hasDefinitions() ) { if ($def->hasDefinitions() ) {
$slotName = eval("return $name;"); $slotName = eval("return $name;");
$def->currentSlot = $slotName; $def->currentSlot = $slotName;
@@ -62,7 +64,7 @@ class BlockToken implements ControlStructure {
} }
return <<<PHP return <<<PHP
<?php \$this->printSlot($name, function($definition array \$___using = []) use (\$picea) { extract(\$___using, \EXTR_SKIP); ?> <?php \$this->printSlot($name, function($definition array \$___using = []) use (\$picea $loops) { extract(\$___using, \EXTR_SKIP); ?>
PHP; PHP;
} }
else { else {
@@ -71,7 +73,7 @@ class BlockToken implements ControlStructure {
} }
return <<<PHP return <<<PHP
<?php (\$___block ?? \$this)->slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea) { extract(\$___using, \EXTR_SKIP); ?> <?php (\$___block ?? \$this)->slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea $loops) { extract(\$___using, \EXTR_SKIP); ?>
PHP; PHP;
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class BreakToken implements ControlStructure {
public string $token = "break"; public string $token = "break";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php break; ?>"; return "<?php break; ?>";
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class CaseToken implements ControlStructure {
public string $token = "case"; public string $token = "case";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
$output = ""; $output = "";
if ( $context->switchStack ) { if ( $context->switchStack ) {
+1 -1
View File
@@ -6,7 +6,7 @@ class ContinueToken implements ControlStructure {
public string $token = "continue"; public string $token = "continue";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php continue; ?>"; return "<?php continue; ?>";
} }
+1 -1
View File
@@ -3,5 +3,5 @@
namespace Picea\ControlStructure; namespace Picea\ControlStructure;
interface ControlStructure { interface ControlStructure {
public function parse(\Picae\Compiler\Context &$context, string $sourceCode, string $token); public function parse(\Picea\Compiler\Context &$context, string $sourceCode, string $token);
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class DefaultToken implements ControlStructure {
public string $token = "default"; public string $token = "default";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
$output = ""; $output = "";
if ( $context->switchStack ) { if ( $context->switchStack ) {
+28
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 ?>";
}
}
-12
View File
@@ -1,12 +0,0 @@
<?php
namespace Picea\ControlStructure;
class EndRawToken implements ControlStructure {
public string $token = "endraw";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) {
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ class ExtendsToken implements ControlStructure {
public string $token = "extends"; public string $token = "extends";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $path, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $path, string $token) {
# Triming string's quotes # Triming string's quotes
$path = trim($path, "\"\' \t"); $path = trim($path, "\"\' \t");
+1 -1
View File
@@ -6,7 +6,7 @@ class ForToken implements ControlStructure {
public array $token = [ "for", "endfor" ]; public array $token = [ "for", "endfor" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) { switch($token) {
case "for": case "for":
$uid = "$".uniqid("for_"); $uid = "$".uniqid("for_");
+2 -40
View File
@@ -2,46 +2,8 @@
namespace Picea\ControlStructure; namespace Picea\ControlStructure;
class ForeachToken implements ControlStructure { use DI\Definition\Source\AnnotationBasedAutowiring;
class ForeachToken extends AbstractLoop {
public array $token = [ "foreach", "endforeach" ]; public array $token = [ "foreach", "endforeach" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) {
switch($token) {
case "foreach":
$name = "$".uniqid("foreach_");
$stack = array_filter($context->iterationStack ?? [], function($item) {
return ! $item['or'];
});
$count = count($stack);
if ( $count > 0 ) {
$name .= "[" . end($stack)['uid'] . "]";
}
$context->iterationStack[] = [
'or' => false,
'uid' => $name,
'token' => 'endforeach',
];
return "<?php foreach ($arguments): $name = ( $name ?? 0 ) + 1; ; ?>";
case "endforeach":
$last = end($context->iterationStack);
if ( $last['or'] === false ) {
$output = "<?php endforeach; ?>";
}
else {
$output = "<?php endif; unset({$last['uid']}) ?>";
}
array_pop($context->iterationStack);
return $output;
}
}
} }
+3 -2
View File
@@ -6,9 +6,11 @@ class FunctionToken implements ControlStructure {
public array $token = [ "function", "endfunction", "return" ]; public array $token = [ "function", "endfunction", "return" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) { switch($token) {
case "function": case "function":
$context->functions++;
return $this->printFunction($context, $arguments); return $this->printFunction($context, $arguments);
case "return": case "return":
@@ -31,7 +33,6 @@ class FunctionToken implements ControlStructure {
protected function printFunction($context, ?string $arguments) : string protected function printFunction($context, ?string $arguments) : string
{ {
$context->functions++;
return "<?php function $arguments { ?>"; return "<?php function $arguments { ?>";
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class IfToken implements ControlStructure {
public array $token = [ "if", "else", "elseif", "endif" ]; public array $token = [ "if", "else", "elseif", "endif" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) { switch($token) {
case "if": case "if":
return "<?php if ($arguments): ?>"; return "<?php if ($arguments): ?>";
+2 -2
View File
@@ -6,7 +6,7 @@ class IncludeToken implements ControlStructure {
public string $token = "include"; public string $token = "include";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ? string $viewPath, string $token) {
return "<?php echo \$___class__template->picea->inlineContent($arguments); ?>"; return "<?php echo \$___class__template->picea->inlineContent($viewPath); ?>";
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class NamespaceToken implements ControlStructure {
public string $token = "namespace"; public string $token = "namespace";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
$context->namespace = $arguments; $context->namespace = $arguments;
return ""; return "";
} }
+2 -2
View File
@@ -6,9 +6,9 @@ class OrToken implements ControlStructure {
public string $token = "or"; public string $token = "or";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
if ( empty($context->iterationStack) ) { if ( empty($context->iterationStack) ) {
throw new \LogicException("Token `or` was used outside of iterator. Make sure your `for` or `foreach` declaration are properly made."); throw new \LogicException("Token `or` was used outside of iterator. Make sure your `for`, `foreach`, `while`, `do/until` declaration are properly made.");
} }
$key = count( $context->iterationStack ) - 1; $key = count( $context->iterationStack ) - 1;
-12
View File
@@ -1,12 +0,0 @@
<?php
namespace Picea\ControlStructure;
class RawToken implements ControlStructure {
public string $token = "raw";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) {
}
}
+32 -8
View File
@@ -6,11 +6,21 @@ class SectionToken implements ControlStructure {
public array $token = [ "section", "endsection" ]; public array $token = [ "section", "endsection" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token)
{
$opt = $context->tokenOptions($token);
if (in_array('prepend', $opt)) {
$context->sectionAction = "prepend";
}
elseif (in_array('append', $opt)) {
$context->sectionAction = "append";
}
switch($token) { switch($token) {
case "section": case "section":
return $this->printSection($context, $arguments); return $this->printSection($context, $arguments);
case "endsection": case "endsection":
if ( empty($context->sections) ) { if ( empty($context->sections) ) {
throw new \RuntimeException("A section closing tag {% endsection %} was found without an opening {% section %} tag"); throw new \RuntimeException("A section closing tag {% endsection %} was found without an opening {% section %} tag");
@@ -28,7 +38,7 @@ class SectionToken implements ControlStructure {
$options = eval("return $options;"); $options = eval("return $options;");
} }
if ( ! ctype_alnum(str_replace([".", "\"", "'", "-", "_"], "", $name)) ) { if ( ! ctype_alnum(str_replace([".", "\"", "'", "-", "_", ":"], "", $name)) ) {
throw new \RuntimeException("Your section named `{$name}` contains invalid character. Allowed are only letters, numbers, dashes, underscores and dots"); throw new \RuntimeException("Your section named `{$name}` contains invalid character. Allowed are only letters, numbers, dashes, underscores and dots");
} }
@@ -37,7 +47,9 @@ class SectionToken implements ControlStructure {
'options' => $options, 'options' => $options,
]; ];
$action = $options['action'] ?? "default"; $action = $options['action'] ?? $context->sectionAction ?? "default";
unset($context->sectionAction);
if (! in_array($action, ['prepend', 'append', 'default'])) { if (! in_array($action, ['prepend', 'append', 'default'])) {
throw new \RuntimeException("An unsupported action `$action` was given as an option of a {% section %} tag"); throw new \RuntimeException("An unsupported action `$action` was given as an option of a {% section %} tag");
@@ -46,14 +58,26 @@ 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'][] = [
"extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); \$___class__template->sectionStack[] = '$name'; ?>"; 'order' => $order,
'callback' => function() use (\$picea, \$___class__template, \$___global_variables, \$___variables, \$__event) {".
"extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE);
\$___class__template->sectionStack[] = $name;
\$__event->eventExecute(\Picea\Event\Builder\ClassTemplateRenderSection::class, $name);?>";
} }
protected function printEndSection($context) : string protected function printEndSection($context) : string
{ {
$section = array_pop($context->sections); $section = array_pop($context->sections);
$build = $context->extendFrom ? "!empty(\$___class__template->sectionStack) && \$___class__template->renderSection({$section['name']});" : "\$___class__template->renderSection({$section['name']});"; $build = $context->extendFrom ? "!empty(\$___class__template->sectionStack) && \$___class__template->renderSection({$section['name']}, false);" : "\$___class__template->renderSection({$section['name']}, false);";
return "<?php array_pop(\$___class__template->sectionStack); }]; $build?>";
return <<<PHP
<?php
\$__event->eventExecute(\Picea\Event\Builder\ClassTemplateRenderSectionDone::class, {$section['name']});
array_pop(\$___class__template->sectionStack); }];
$build
?>
PHP;
} }
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class SwitchToken implements ControlStructure {
public array $token = [ "switch", "case", "endswitch" ]; public array $token = [ "switch", "case", "endswitch" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) { switch($token) {
case "switch": case "switch":
$context->switchStack[] = true; $context->switchStack[] = true;
+1 -1
View File
@@ -6,7 +6,7 @@ class UseToken implements ControlStructure {
public string $token = "use"; public string $token = "use";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
$context->useStack[] = $arguments; $context->useStack[] = $arguments;
return ""; return "";
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class ViewToken implements ControlStructure {
public string $token = "view"; public string $token = "view";
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
# The way this is ordered, if you provide a second arguments, being an array of variables, get_defined_vars() will not be pushed inside the view # The way this is ordered, if you provide a second arguments, being an array of variables, get_defined_vars() will not be pushed inside the view
return "<?php echo \$___class__template->picea->inlineHtml(\$this, $arguments, get_defined_vars()); ?>"; return "<?php echo \$___class__template->picea->inlineHtml(\$this, $arguments, get_defined_vars()); ?>";
} }
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Picea\ControlStructure;
class WhileToken extends AbstractLoop {
public array $token = [ "do", "while", "endwhile", ];
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) {
case "do":
$context->iterationStack[] = [
'or' => false,
'token' => "do",
];
return "<?php do { ?>";
case "while":
if ( $context->iterationStack ?? false ) {
if ( end($context->iterationStack)['token'] === 'do' ) {
array_pop($context->iterationStack);
return "<?php } while($arguments); ?>";
}
}
}
return parent::parse($context, $arguments, $token);
}
}
@@ -0,0 +1,7 @@
<?php
namespace Picea\Event\Builder;
interface ClassTemplateOutputDone {
public function execute(string $name, array $variablesList) : mixed;
}
@@ -0,0 +1,7 @@
<?php
namespace Picea\Event\Builder;
interface ClassTemplateOutputing {
public function execute(string $name, array $variablesList) : mixed;
}
@@ -0,0 +1,7 @@
<?php
namespace Picea\Event\Builder;
interface ClassTemplateRenderSection {
public function execute(string $name) : mixed;
}
@@ -0,0 +1,7 @@
<?php
namespace Picea\Event\Builder;
interface ClassTemplateRenderSectionDone {
public function execute(string $section) : mixed;
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Picea;
trait EventTrait
{
protected $_eventTraitMethod = "execute";
public array $_eventList = [];
protected array $_returnList = [];
public function eventRegister(object $event) : void
{
$this->_eventList[] = $event;
}
public function eventFromType(string $type) : array
{
return array_filter($this->_eventList, fn($ev) => $ev instanceof $type);
}
public function eventExecute(string $type, ...$arguments) : void
{
foreach($this->eventFromType($type) as $event) {
$this->_returnList[$event::class][] = call_user_func_array([ $event, $this->_eventTraitMethod ], $arguments);
}
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace Picea\Exception;
use Picea\Picea;
class RegisterExtensionToken extends \Exception {}
+52
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;
}
}
-129
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;
}
}
}
}
}
}
+1 -1
View File
@@ -3,5 +3,5 @@
namespace Picea\Extension; namespace Picea\Extension;
interface Extension { interface Extension {
public function parse(\Picae\Compiler\Context &$context, string $sourceCode, string $token); public function parse(\Picea\Compiler\Context &$context, string $sourceCode, string $token);
} }
+10 -9
View File
@@ -6,21 +6,22 @@ use Picea\Compiler\Context;
class JsonExtension implements Extension, FunctionExtension { class JsonExtension implements Extension, FunctionExtension {
public array $token = [ "json", "json.pretty", "json.html" ]; public array $token = [ "json" ];
public int $flags = JSON_HEX_TAG | \JSON_HEX_QUOT | \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_UNICODE; public int $flags = JSON_HEX_TAG | \JSON_HEX_QUOT | \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_UNICODE;
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token)
{ {
$opt = $context->tokenOptions($token);
$flag = $this->flags; $flag = $this->flags;
switch ($token) { if ( in_array('pretty', $opt) ) {
case "json.pretty": $flag |= \JSON_PRETTY_PRINT;
$flag |= \JSON_PRETTY_PRINT; }
break;
case "json.html": if ( in_array('html', $opt) ) {
return "<?php echo htmlentities(json_encode($arguments, {$this->flags}), ENT_QUOTES, 'UTF-8') ?>"; return "<?php echo htmlentities(json_encode($arguments, $flag), ENT_QUOTES, 'UTF-8') ?>";
} }
$cls = static::class; $cls = static::class;
@@ -32,7 +33,7 @@ class JsonExtension implements Extension, FunctionExtension {
{ {
return [ return [
'json' => function($arguments, ? int $flags = null) { 'json' => function($arguments, ? int $flags = null) {
return json_encode($arguments, \JSON_FORCE_OBJECT); return json_encode($arguments, $flags ?? $this->flags);
}, },
]; ];
} }
+48 -11
View File
@@ -3,10 +3,11 @@
namespace Picea\Extension; namespace Picea\Extension;
use Picea\Compiler\Context; use Picea\Compiler\Context;
use Picea\Event\Builder\ClassTemplateRenderSectionDone;
class LanguageExtension implements Extension, FunctionExtension { class LanguageExtension implements Extension, FunctionExtension {
public array $tokens = [ "lang", "lang.raw", "_", "_.raw", "language.set" ]; public array $tokens = [ "lang", "_", "language" ];
public string $currentLanguage = ""; public string $currentLanguage = "";
@@ -24,23 +25,42 @@ class LanguageExtension implements Extension, FunctionExtension {
]; ];
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
{ {
switch($token) { $opt = $context->tokenOptions($token);
case "language.set":
return "<?php \$picea->compiler->getExtensionFromToken('$token')->currentLanguage = $arguments; ?>"; switch(explode('.', $token)[0]) {
case "language":
$cls = $this::class;
return <<<PHP
<?php
(function(\\$cls \$ext) use (\$___class__template, \$__event) {
#\$extends = \$___class__template->getParam('extends');
if ( \$___class__template->depth === 1 || \$___class__template->sectionStack ) {
\$ext->currentLanguage = $arguments;
# @TODO Make sure this event is only registered when we output() a template, if we are in a section, we must attach it a view/section/block output event instead !
\$__event->eventRegister(\\$cls::outputDoneEvent(\$ext));
}
})(\$picea->compiler->getExtensionFromToken('$token'));
?>
PHP;
case "lang": case "lang":
if ( in_array('raw', $opt) ) {
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments) ?>";
}
return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>"; return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>";
case "lang.raw":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments) ?>";
case "_": case "_":
return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>"; if ( in_array('raw', $opt) ) {
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>";
}
case "_.raw": return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>";
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>";
} }
return ""; return "";
@@ -59,4 +79,21 @@ class LanguageExtension implements Extension, FunctionExtension {
{ {
return $this->languageHandler->languageFromKey($key, $variables); return $this->languageHandler->languageFromKey($key, $variables);
} }
public static function outputDoneEvent(LanguageExtension $languageExtension) : ClassTemplateRenderSectionDone
{
return new class($languageExtension) implements ClassTemplateRenderSectionDone {
protected string $current;
public function __construct(protected LanguageExtension $languageExtension) {
$this->current = $this->languageExtension->currentLanguage;
}
public function execute(string $name) : mixed
{
return $this->current ? $this->languageExtension->currentLanguage = $this->current : null;
}
};
}
} }
+8 -7
View File
@@ -4,7 +4,7 @@ namespace Picea\Extension;
use Picea\Compiler\Context; use Picea\Compiler\Context;
class MoneyExtension implements Extension { class MoneyExtension implements Extension, FunctionExtension {
public string $token = "money"; public string $token = "money";
@@ -14,18 +14,19 @@ class MoneyExtension implements Extension {
public \NumberFormatter $formatter; public \NumberFormatter $formatter;
public function __construct(Context $context) { public function __construct() {
$this->register($context);
$this->locale = explode('.', \Locale::getDefault())[0]; $this->locale = explode('.', \Locale::getDefault())[0];
$this->formatter = new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY); $this->formatter = new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY);
} }
public function register(Context $context) : void public function exportFunctions(): array
{ {
$context->pushFunction("money", [ $this, 'money' ]); return [
"money" => [ $this, 'money' ]
];
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php echo money($arguments) ?>"; return "<?php echo money($arguments) ?>";
} }
+1 -1
View File
@@ -6,7 +6,7 @@ class PhpExtension implements Extension {
public array $token = [ "php" ]; public array $token = [ "php" ];
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php $arguments ?>"; return "<?php $arguments ?>";
} }
} }
+16 -7
View File
@@ -6,7 +6,7 @@ use Picea\Compiler\Context;
class PrintExtension implements Extension { class PrintExtension implements Extension {
public array $token = [ "print", "print.safe", "print.raw" ]; public array $token = [ "echo", "print", "printf" ];
public int $flag = \ENT_QUOTES; public int $flag = \ENT_QUOTES;
@@ -18,15 +18,24 @@ class PrintExtension implements Extension {
$this->encoding = ini_get("default_charset"); $this->encoding = ini_get("default_charset");
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string
{ {
switch($token) { $opt = $context->tokenOptions($token);
case 'print':
case "print.safe":
return "<?php echo htmlspecialchars(sprintf($arguments), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
switch($token) {
case 'echo':
case 'print':
return "<?php echo htmlspecialchars((string) $arguments, {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
case 'echo.raw':
case "print.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) ?>";
} }
} }
+15 -13
View File
@@ -2,25 +2,27 @@
namespace Picea\Extension; namespace Picea\Extension;
use Picea\Compiler\Context; class TitleExtension implements Extension, FunctionExtension {
class TitleExtension implements Extension {
public string $token = "title"; public string $token = "title";
public string $title = ""; public string $title = "";
public function __construct(Context $context) { public function exportFunctions(): array
$this->register($context); {
return [
"title" => [$this, 'handleTitle'],
];
} }
public function register(Context $context) : void public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
{ return <<<PHP
$context->pushFunction("title", [ $this, 'handleTitle' ]); <?php
} if ( null !== \$title = title($arguments) ) {
echo \$title;
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { }
return "<?php echo title($arguments) ?>"; ?>
PHP;
} }
public function handleTitle(? string $set = null, ...$arguments) : ? string public function handleTitle(? string $set = null, ...$arguments) : ? string
+31 -22
View File
@@ -4,7 +4,7 @@ namespace Picea\Extension;
use Picea\Compiler\Context; use Picea\Compiler\Context;
class UrlExtension implements Extension { class UrlExtension implements Extension, FunctionExtension {
public const URLIZE_PATTERN_URL = <<<PATTERN public const URLIZE_PATTERN_URL = <<<PATTERN
~(?<!href=['"])https?://[\w/._\-&?]*(?!</a>)(?=[^\w/._\-&])~s ~(?<!href=['"])https?://[\w/._\-&?]*(?!</a>)(?=[^\w/._\-&])~s
@@ -22,16 +22,17 @@ PATTERN;
protected array $routesTarget; protected array $routesTarget;
public array $tokens = [ "url" , "route", "route.cacheless", "asset", "url.current", "url.parameters", "slug" ]; public array $tokens = [ "url" , "route", "asset", "slug" ];
public function __construct(Context $context, string $urlBase = "", string $assetToken = "") { public function __construct(string $urlBase = "", string $assetToken = "") {
$this->urlBase = trim($urlBase, "/"); $this->urlBase = trim($urlBase, "/");
$this->assetToken = $assetToken; $this->assetToken = $assetToken;
$this->register($context);
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : ?string public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : ?string
{ {
$opt = $context->tokenOptions($token);
switch($token) { switch($token) {
case "asset": case "asset":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildAssetUrl($arguments) ?>"; return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildAssetUrl($arguments) ?>";
@@ -40,29 +41,32 @@ PATTERN;
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildRouteUrl($arguments) ?>"; return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildRouteUrl($arguments) ?>";
case "url": case "url":
if ( in_array('parameters', $opt) ) {
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>";
}
elseif ( in_array('current', $opt) ) {
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->currentUrl($arguments) ?>";
}
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>"; return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>";
case "url.current":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->currentUrl($arguments) ?>";
case "url.parameters":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>";
case "slug": case "slug":
return \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate( $arguments ); return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->slug($arguments) ?>";
} }
return null; return null;
} }
public function register(Context $context) : void public function exportFunctions(): array
{ {
$context->pushFunction("url", [ $this, 'buildUrl' ]); return [
$context->pushFunction("current_url", [ $this, 'currentUrl' ]); "url" => [ $this, 'buildUrl' ],
$context->pushFunction("asset", [ $this, 'buildAssetUrl' ]); "current_url" => [ $this, 'currentUrl' ],
$context->pushFunction("route", [ $this, 'buildRouteUrl' ]); "asset" => [ $this, 'buildAssetUrl' ],
$context->pushFunction("slug", [ $this, 'slug' ]); "route" => [ $this, 'buildRouteUrl' ],
$context->pushFunction("urlize", [ $this, 'urlize' ]); "slug" => [ $this, 'slug' ],
"urlize" => [ $this, 'urlize' ]
];
} }
public function getRouteList(bool $full = false) : array public function getRouteList(bool $full = false) : array
@@ -120,9 +124,14 @@ PATTERN;
return $this->scheme() . $this->domain() . $this->base(); return $this->scheme() . $this->domain() . $this->base();
} }
# src: https://stackoverflow.com/a/14550919
public static function slug(string $text, string $separator = '-') : string public static function slug(string $text, string $separator = '-') : string
{ {
return trim(str_replace('-', $separator, \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate(str_replace('-', ' ', $text))), '-'); $clean = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
$clean = preg_replace("/[^a-zA-Z0-9\/_| -]/", '', $clean);
return preg_replace("/[\/_| -]+/", $separator, strtolower(trim($clean, '-')));
} }
public function registerRoute(string $name, string $route, string $class, string $method, array $routeMethods) : void public function registerRoute(string $name, string $route, string $class, string $method, array $routeMethods) : void
@@ -194,7 +203,7 @@ PATTERN;
$_SERVER['Front-End-Https'] ?? "", $_SERVER['Front-End-Https'] ?? "",
$_SERVER['X-Forwarded-Proto'] ?? "", $_SERVER['X-Forwarded-Proto'] ?? "",
$_SERVER['X-Forwarded-Protocol'] ?? "", $_SERVER['X-Forwarded-Protocol'] ?? "",
$_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "", $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "",
$_SERVER['HTTP_X_FORWARDED_PROTOCOL'] ?? "", $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] ?? "",
])) || isset($_SERVER['HTTP_X_ARR_SSL']); ])) || isset($_SERVER['HTTP_X_ARR_SSL']);
+2
View File
@@ -44,10 +44,12 @@ class DefaultRegistrations implements LanguageRegistration
$compiler->registerControlStructure(new \Picea\ControlStructure\IfToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\IfToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\ForeachToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ForeachToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\ForToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ForToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\WhileToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\OrToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\OrToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\SwitchToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\SwitchToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\DefaultToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\DefaultToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\BreakToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\BreakToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\ContinueToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\ExtendsToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ExtendsToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\SectionToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\SectionToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\FunctionToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\FunctionToken());
+12 -10
View File
@@ -7,10 +7,11 @@ use Picea\Extension\Extension,
use Picea\Compiler\Context; use Picea\Compiler\Context;
use Picea\Extension\FunctionExtension;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
class Request implements Extension { class Request implements Extension, FunctionExtension {
use ExtensionTrait; use ExtensionTrait;
public array $tokens; public array $tokens;
@@ -21,20 +22,21 @@ class Request implements Extension {
public function __construct(ServerRequestInterface $request, Context $context) { public function __construct(ServerRequestInterface $request, Context $context) {
$this->request = $request; $this->request = $request;
$this->register($context);
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string { } public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string { }
public function register(Context $context) : void public function exportFunctions(): array
{ {
$context->pushFunction("cookie", [ $this, 'cookie' ]); return [
$context->pushFunction("get", [ $this, 'get' ]); "cookie" => [ $this, 'cookie' ],
$context->pushFunction("post", [ $this, 'post' ]); "get" => [ $this, 'get' ],
$context->pushFunction("request", [ $this, 'request' ]); "post" => [ $this, 'post' ],
$context->pushFunction("server", [ $this, 'server' ]); "request" => [ $this, 'request' ],
"server" => [ $this, 'server' ],
];
} }
public function cookie(? string $variableName = null, $default = null) public function cookie(? string $variableName = null, $default = null)
{ {
return $variableName === null ? $this->request->getCookieParams() : static::arrayGet($this->request->getCookieParams(), $variableName) ?? $default; return $variableName === null ? $this->request->getCookieParams() : static::arrayGet($this->request->getCookieParams(), $variableName) ?? $default;
+10 -52
View File
@@ -20,8 +20,6 @@ class Picea implements LanguageRegistration
public string $builderTemplatePath; public string $builderTemplatePath;
public Closure $responseHtml;
public Caching\Cache $cache; public Caching\Cache $cache;
public FileFetcher $fileFetcher; public FileFetcher $fileFetcher;
@@ -35,7 +33,6 @@ class Picea implements LanguageRegistration
public array $compiled = []; public array $compiled = [];
public function __construct( public function __construct(
? Closure $responseHtml = null,
? Compiler\Context $context = null, ? Compiler\Context $context = null,
? Caching\Cache $cache = null, ? Caching\Cache $cache = null,
? Compiler $compiler = null, ? Compiler $compiler = null,
@@ -44,7 +41,6 @@ class Picea implements LanguageRegistration
? string $builderTemplatePath = null, ? string $builderTemplatePath = null,
bool $debug = false bool $debug = false
){ ){
$this->response = $responseHtml;
$this->cache = $cache ?? new Caching\Memory(""); $this->cache = $cache ?? new Caching\Memory("");
$this->context = $context ?? new Compiler\BaseContext(); $this->context = $context ?? new Compiler\BaseContext();
$this->languageRegistration = $languageRegistration ?? new Language\DefaultRegistrations(); $this->languageRegistration = $languageRegistration ?? new Language\DefaultRegistrations();
@@ -58,7 +54,7 @@ class Picea implements LanguageRegistration
$this->renderContext($this->context); $this->renderContext($this->context);
} }
public function gatherTemplateObject(string $viewPath, array $variables = [], ? object $proxy = null) : ? object public function gatherCompiledObject(string $viewPath, array $variables = [], ? object $proxy = null) : ? object
{ {
if ( null === $object = $this->fetchFromCache($viewPath, $this->globalVariables + $variables, $proxy) ) { if ( null === $object = $this->fetchFromCache($viewPath, $this->globalVariables + $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.");
@@ -69,59 +65,21 @@ class Picea implements LanguageRegistration
public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : ? string public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : ? string
{ {
$object = $this->gatherTemplateObject($viewPath, $variables, $proxy); $object = $this->gatherCompiledObject($viewPath, $variables, $proxy);
try { try {
return call_user_func($object); return call_user_func($object);
} }
catch(\Throwable $ex) { catch(\Throwable $ex) {
#throw $ex; if (! $ex instanceof Exception\RenderHtmlException ) {
# Temporary class for an experiment on retrieving more accurate debug info throw new Exception\RenderHtmlException($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex);
throw new class($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex) extends \Exception { }
else {
protected Picea $picea; throw $ex;
}
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;
}
};
} }
exit();
} }
/** /**
+1 -1
View File
@@ -10,7 +10,7 @@ class CommentToken implements Syntax {
protected string $tokenClose = "\#\}"; protected string $tokenClose = "\#\}";
public function parse(/*\Picae\Compiler\Context*/ &$context, string &$sourceCode) public function parse(\Picea\Compiler\Context &$context, string &$sourceCode)
{ {
$sourceCode = preg_replace("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", "", $sourceCode); $sourceCode = preg_replace("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", "", $sourceCode);
} }
+1 -1
View File
@@ -8,7 +8,7 @@ class EchoRawToken implements Syntax {
public string $tokenClose = "\}\}"; public string $tokenClose = "\}\}";
public function parse(/*\Picae\Compiler\Context*/ &$content, string &$sourceCode) public function parse(\Picea\Compiler\Context &$content, string &$sourceCode)
{ {
$sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) { $sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) {
$line = trim($matches[2], " \t\n\r\0\x0B;"); $line = trim($matches[2], " \t\n\r\0\x0B;");
+1 -1
View File
@@ -18,7 +18,7 @@ class EchoSafeToken implements Syntax {
$this->encoding = ini_get("default_charset"); $this->encoding = ini_get("default_charset");
} }
public function parse(/*\Picae\Compiler\Context*/ &$context, string &$sourceCode) public function parse(\Picea\Compiler\Context &$context, string &$sourceCode)
{ {
$sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) { $sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) {
$line = trim($matches[2], " \t\n\r\0\x0B;"); $line = trim($matches[2], " \t\n\r\0\x0B;");
+1 -1
View File
@@ -3,5 +3,5 @@
namespace Picea\Syntax; namespace Picea\Syntax;
interface Syntax { interface Syntax {
public function parse(\Picae\Compiler\Context &$context, string &$sourceCode); public function parse(\Picea\Compiler\Context &$context, string &$sourceCode);
} }