Compare commits
	
		
			2 Commits
		
	
	
		
			33298ff2e4
			...
			46535c2734
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 46535c2734 | |||
| bb29a56077 | 
							
								
								
									
										47
									
								
								docs/00-intro.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								docs/00-intro.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										26
									
								
								docs/01-comment.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										73
									
								
								docs/01-echoing.md
									
									
									
									
									
										Normal 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 <safe> content", 3, 'UTF-8', true) ?>
 | 
			
		||||
 | 
			
		||||
<?php echo htmlspecialchars((string) "This is another way to output <safe> 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>
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										86
									
								
								docs/02-control-structure-comparisons.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								docs/02-control-structure-comparisons.md
									
									
									
									
									
										Normal 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; ?>
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										122
									
								
								docs/02-control-structure-extends-section.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								docs/02-control-structure-extends-section.md
									
									
									
									
									
										Normal 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" %}
 | 
			
		||||
                © 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">
 | 
			
		||||
            © 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">
 | 
			
		||||
            © Copyright 2022
 | 
			
		||||
        </footer>
 | 
			
		||||
    </body>
 | 
			
		||||
</html>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Notice that the `<header>` tag is now empty, since we've redeclared it in our navless view.
 | 
			
		||||
							
								
								
									
										42
									
								
								docs/02-control-structure-function.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								docs/02-control-structure-function.md
									
									
									
									
									
										Normal 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
									
								
								docs/02-control-structure-loops.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								docs/02-control-structure-loops.md
									
									
									
									
									
										Normal 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
									
								
								docs/02-control-structure-use.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/02-control-structure-use.md
									
									
									
									
									
										Normal 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>
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										112
									
								
								docs/02-control-structure-view-include-block.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								docs/02-control-structure-view-include-block.md
									
									
									
									
									
										Normal 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>
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										0
									
								
								docs/10-extension-json.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-json.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								docs/10-extension-language.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-language.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								docs/10-extension-money.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-money.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								docs/10-extension-php.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-php.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								docs/10-extension-title.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/10-extension-title.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
<?php
 | 
			
		||||
							
								
								
									
										0
									
								
								docs/10-extension-url.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-url.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										82
									
								
								docs/99-dependency-injection.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								docs/99-dependency-injection.md
									
									
									
									
									
										Normal 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());
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@ -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 ) {
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,8 @@ abstract class Context {
 | 
			
		||||
 | 
			
		||||
    public array $useStack = [];
 | 
			
		||||
 | 
			
		||||
    public int $functions = 0;
 | 
			
		||||
    
 | 
			
		||||
    public array $functionStack = [];
 | 
			
		||||
 | 
			
		||||
    public array $hooks = [];
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								src/ControlStructure/AbstractLoop.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/ControlStructure/AbstractLoop.php
									
									
									
									
									
										Normal 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@ class BlockToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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 = [];
 | 
			
		||||
 | 
			
		||||
        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))
 | 
			
		||||
                            );
 | 
			
		||||
                        }
 | 
			
		||||
                    ?>
 | 
			
		||||
                    /*%EXCEPTION_LINE_BASE%*/?>
 | 
			
		||||
                PHP;
 | 
			
		||||
            
 | 
			
		||||
            case "define":
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class BreakToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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; ?>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class CaseToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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 = "";
 | 
			
		||||
 | 
			
		||||
        if ( $context->switchStack ) {
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class ContinueToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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; ?>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,5 @@
 | 
			
		||||
namespace Picea\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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class DefaultToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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 = "";
 | 
			
		||||
 | 
			
		||||
        if ( $context->switchStack ) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								src/ControlStructure/EchoToken.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/ControlStructure/EchoToken.php
									
									
									
									
									
										Normal 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 ?>";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@ class ExtendsToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        $path = trim($path, "\"\' \t");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class ForToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            case "for":
 | 
			
		||||
                $uid = "$".uniqid("for_");
 | 
			
		||||
 | 
			
		||||
@ -2,46 +2,8 @@
 | 
			
		||||
 | 
			
		||||
namespace Picea\ControlStructure;
 | 
			
		||||
 | 
			
		||||
class ForeachToken implements ControlStructure {
 | 
			
		||||
use DI\Definition\Source\AnnotationBasedAutowiring;
 | 
			
		||||
 | 
			
		||||
class ForeachToken extends AbstractLoop {
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,11 @@ class FunctionToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            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 { ?>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class IfToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            case "if":
 | 
			
		||||
                return "<?php if ($arguments): ?>";
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class IncludeToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    public string $token = "include";
 | 
			
		||||
 | 
			
		||||
    public function parse(/*\Picae\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); ?>";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class NamespaceToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,9 @@ class OrToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) ) {
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@ class SectionToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        switch($token) {
 | 
			
		||||
            case "section":
 | 
			
		||||
                return $this->printSection($context, $arguments);
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class SwitchToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            case "switch":
 | 
			
		||||
                $context->switchStack[] = true;
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class UseToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class ViewToken implements ControlStructure {
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        return "<?php echo \$___class__template->picea->inlineHtml(\$this, $arguments, get_defined_vars()); ?>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								src/ControlStructure/WhileToken.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/ControlStructure/WhileToken.php
									
									
									
									
									
										Normal 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								src/Exception/RenderHtmlException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/Exception/RenderHtmlException.php
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -3,5 +3,5 @@
 | 
			
		||||
namespace Picea\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,7 +10,7 @@ class JsonExtension implements Extension, FunctionExtension {
 | 
			
		||||
    
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        $flag = $this->flags;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ 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) {
 | 
			
		||||
            case "language.set":
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ class MoneyExtension implements Extension {
 | 
			
		||||
        $context->pushFunction("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) ?>";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ class PhpExtension implements Extension {
 | 
			
		||||
 | 
			
		||||
    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 ?>";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -18,15 +18,22 @@ class PrintExtension implements Extension {
 | 
			
		||||
        $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) {
 | 
			
		||||
            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) ?>";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ class TitleExtension implements Extension {
 | 
			
		||||
        $context->pushFunction("title", [ $this, 'handleTitle' ]);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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 title($arguments) ?>";
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ PATTERN;
 | 
			
		||||
        $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
 | 
			
		||||
    {
 | 
			
		||||
        switch($token) {
 | 
			
		||||
            case "asset":
 | 
			
		||||
 | 
			
		||||
@ -44,10 +44,12 @@ class DefaultRegistrations implements LanguageRegistration
 | 
			
		||||
        $compiler->registerControlStructure(new \Picea\ControlStructure\IfToken());
 | 
			
		||||
        $compiler->registerControlStructure(new \Picea\ControlStructure\ForeachToken());
 | 
			
		||||
        $compiler->registerControlStructure(new \Picea\ControlStructure\ForToken());
 | 
			
		||||
        $compiler->registerControlStructure(new \Picea\ControlStructure\WhileToken());
 | 
			
		||||
        $compiler->registerControlStructure(new \Picea\ControlStructure\OrToken());
 | 
			
		||||
        $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());
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ class Request implements Extension {
 | 
			
		||||
        $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
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ class CommentToken implements Syntax {
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ class EchoRawToken implements Syntax {
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
            $line = trim($matches[2], " \t\n\r\0\x0B;");
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ class EchoSafeToken implements Syntax {
 | 
			
		||||
        $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) {
 | 
			
		||||
            $line = trim($matches[2], " \t\n\r\0\x0B;");
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,5 @@
 | 
			
		||||
namespace Picea\Syntax;
 | 
			
		||||
 | 
			
		||||
interface Syntax {
 | 
			
		||||
    public function parse(\Picae\Compiler\Context &$context, string &$sourceCode);
 | 
			
		||||
    public function parse(\Picea\Compiler\Context &$context, string &$sourceCode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user