31 Commits
Author SHA1 Message Date
mcndave acd9f8c72e - First task of v2 done, tokens can now have unlimited options attached to it 2023-02-02 16:46:46 +00:00
mcndave afeb8789b5 - Added a new doc section about both new tokens 'section.prepend/append' 2023-02-01 18:20:42 +00:00
mcndave ded3d5e907 Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2023-02-01 18:09:01 +00:00
mcndave 9ea4856f49 - Added tokens 'section.append' and 'section.prepend' which equates longer form '{% section mysection, [ "action" => "prepend" ] %}' 2023-02-01 18:08:47 +00:00
mcndave 84020a871c Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2023-02-01 18:06:32 +00:00
mcndave 2dc16de7c3 - Simplified SLUG() method of the UrlExtension, since it was way too slow 2023-02-01 18:06:23 +00:00
mcndave af212b07ca Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2023-01-26 13:36:49 +00:00
mcndave 58766decaf - Fixed a bug within sections if it doesn't exist first 2023-01-26 13:36:38 +00:00
mcndave 08211282c2 - Added an event manager. Also, fixed some more bugs within Block and Section tokens 2023-01-26 13:32:52 +00:00
mcndave 58f73d914b - Some more documentation made / fixed 2022-12-22 14:35:26 +00:00
mcndave 46535c2734 - Finalizing the documentation before tagging 1.0 release 2022-12-21 04:20:11 +00:00
mcndave bb29a56077 - Added while/endwhile and do/while syntaxes
- Fix the abstractclass requirement of parse() function
2022-12-19 19:36:39 +00:00
mcndave 33298ff2e4 - Removed optional brackets from routes in UrlExtension. 2022-12-08 19:10:43 +00:00
mcndave 3de5f46de6 Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2022-11-29 19:46:51 +00:00
mcndave 4bd4cddbff - Cleaned up a bit and added some methods to give Lean/Console more flexibility 2022-11-29 19:46:40 +00:00
mcndave 7e80d4be6d - Added an 'urlize' method to the UrlExtension class 2022-11-24 20:00:43 +00:00
mcndave 14cfb46c98 Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2022-10-12 18:28:52 +00:00
mcndave 08201b1958 - Remove the old {? / ?} token as a PHP opening
- Added url versions if needed
2022-10-12 18:25:39 +00:00
mcndave 5bac6cd843 - Rencoding UTF-8 to manage problematics strings in data to encode 2022-10-10 15:27:54 +00:00
mcndave 4c3772376c - Quickfix in block error message output without variables 2022-07-18 18:14:49 +00:00
mcndave aa0bdea0c5 - added a new lang.raw and _.raw token to Lang extexion 2022-07-18 17:44:03 +00:00
mcndave 531f786747 Merge branch 'master' of https://git.mcnd.ca/mcndave/picea 2022-06-27 17:29:31 +00:00
mcndave 22e046dfcd - Fixed post() method 2022-06-27 17:28:35 +00:00
mcndave b08325049b - Addedd a new Money extension and a new Function token 2022-06-03 15:31:50 +00:00
mcndave 48ecd9f7d9 - Made some bugfixes regarding blocks and slots 2022-06-03 15:30:16 +00:00
Ubuntu 18a0fd7fdc - Adjusted some protocol recognition into var to match proxy's one 2022-05-11 15:20:04 +00:00
Dave Mc Nicoll 2b81e9cef8 - Addeed a slug method 2022-05-11 14:09:41 +00:00
Dave Mc Nicoll b836fc5bd8 - added missing json.html tag 2022-02-25 15:32:47 +00:00
Dave Mc Nicoll 45c8173029 - Mostly bugfixes made while working on ParcInfo 2021-10-19 12:39:14 +00:00
mcndave 67040db155 - Added the missing continue token 2021-03-24 13:49:38 +00:00
mcndave 7588748ebd - Global variables were merged by error with the block's arguments 2021-03-15 13:48:31 +00:00
73 changed files with 1644 additions and 405 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());
},
];
```
+1 -1
View File
@@ -4,7 +4,7 @@ namespace Picea;
class Builder class Builder
{ {
const TEMPLATE_CLASSNAME_PREFIX = "PiceaTemplate_"; public const TEMPLATE_CLASSNAME_PREFIX = "PiceaTemplate_";
protected string $templatePath = ""; protected string $templatePath = "";
+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;
}
+5
View File
@@ -29,4 +29,9 @@ interface Cache {
* @return bool * @return bool
*/ */
public function save(Context $context) : bool; public function save(Context $context) : bool;
/**
* Purge path cache
*/
public function purge() : void;
} }
+17 -9
View File
@@ -2,8 +2,7 @@
namespace Picea\Caching; namespace Picea\Caching;
use Picea\Builder, use Picea\ { Builder, Compiler\Context };
Picea\Compiler\Context;
class Opcache implements Cache { class Opcache implements Cache {
@@ -58,10 +57,6 @@ class Opcache implements Cache {
if ( file_exists($path = $this->cachePath($viewPath)) ) { if ( file_exists($path = $this->cachePath($viewPath)) ) {
$this->compiled[$viewPath] = include($path); $this->compiled[$viewPath] = include($path);
# if ( $this->compiled[$viewPath]['extends'] ?? false ) {
# $this->compiled($this->compiled[$viewPath]['extends']);
# }
return true; return true;
} }
@@ -86,7 +81,7 @@ class Opcache implements Cache {
return true; return true;
} }
protected function cachePath(string $fileName = "") : string public function cachePath(string $fileName = "") : string
{ {
return implode(DIRECTORY_SEPARATOR, array_filter([ $this->cachePath, $this->cacheFolder, $fileName ? str_replace([ "/", DIRECTORY_SEPARATOR ], "~", Builder::generateClassName($fileName) . ".php") : null ])); return implode(DIRECTORY_SEPARATOR, array_filter([ $this->cachePath, $this->cacheFolder, $fileName ? str_replace([ "/", DIRECTORY_SEPARATOR ], "~", Builder::generateClassName($fileName) . ".php") : null ]));
} }
@@ -95,7 +90,7 @@ class Opcache implements Cache {
spl_autoload_register(function ($class) use ($namespace) { spl_autoload_register(function ($class) use ($namespace) {
$prefix = "$namespace\\"; $prefix = "$namespace\\";
$baseDir = $this->cachePath() . "/"; $baseDir = $this->cachePath() . DIRECTORY_SEPARATOR;
$len = strlen($prefix); $len = strlen($prefix);
@@ -103,11 +98,24 @@ class Opcache implements Cache {
return; return;
} }
$file = $baseDir . str_replace('\\', '/', substr($class, $len)) . '.php'; $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $len)) . '.php';
if ( file_exists($file) ) { if ( file_exists($file) ) {
require $file; require $file;
} }
}); });
} }
public function getFilelist() : array
{
return glob($this->cachePath() . DIRECTORY_SEPARATOR . Builder::TEMPLATE_CLASSNAME_PREFIX . "*");
}
public function purge() : void
{
foreach($this->getFilelist() as $dir)
{
dump($dir);
}
}
} }
+29 -14
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;
@@ -48,17 +49,18 @@ class Compiler
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 = 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,7 +89,11 @@ 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) {
$this->tagList[$token] = $controlStructureObject; 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;
} }
return $this; return $this;
@@ -98,7 +104,11 @@ class Compiler
$tokens = $extension->tokens ?? (array) ( $extension->token ?? [] ); $tokens = $extension->tokens ?? (array) ( $extension->token ?? [] );
foreach($tokens as $token) { foreach($tokens as $token) {
$this->extensionList[$token] = $extension; 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;
} }
if (! $tokens ) { if (! $tokens ) {
@@ -113,9 +123,7 @@ class Compiler
foreach($this->extensionList as $ext) { foreach($this->extensionList as $ext) {
if ($ext instanceof FunctionExtension) { if ($ext instanceof FunctionExtension) {
foreach ($ext->exportFunctions() as $name => $value) { foreach ($ext->exportFunctions() as $name => $value) {
if ( is_string($value) ) { $callable = is_string($value) ? fn(...$args) => call_user_func_array([ $ext, $value ], $args) : null;
$callable = fn(...$args) => call_user_func_array([ $ext, $value ], $args);
}
$context->pushFunction(is_numeric($name) ? $value : $name, $callable ?? $value); $context->pushFunction(is_numeric($name) ? $value : $name, $callable ?? $value);
} }
@@ -124,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;
}
}
}
+11 -7
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) {
@@ -24,17 +24,16 @@ class BlockToken implements ControlStructure {
return <<<PHP return <<<PHP
<?php <?php
try { try {
extract( \$this->using ?? [], \EXTR_OVERWRITE);
extract( \\$class::parseArguments(function($arguments) {}, \$inlineVariables), \EXTR_OVERWRITE); extract( \\$class::parseArguments(function($arguments) {}, \$inlineVariables), \EXTR_OVERWRITE);
unset(\$inlineVariables); unset(\$inlineVariables);
} }
catch(\TypeError \$ex) { catch(\TypeError \$ex) {
throw new \Exception( throw new \Exception(
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":
@@ -51,6 +50,8 @@ class BlockToken implements ControlStructure {
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;
@@ -63,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 {
@@ -72,7 +73,7 @@ class BlockToken implements ControlStructure {
} }
return <<<PHP return <<<PHP
<?php \$___block->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;
} }
@@ -131,6 +132,9 @@ class BlockToken implements ControlStructure {
elseif ( $value->isVariadic() ) { elseif ( $value->isVariadic() ) {
$parameters[ $value->getName() ] = []; $parameters[ $value->getName() ] = [];
} }
else {
$parameters[ $value->getName() ] = null;
}
} }
return $parameters; return $parameters;
+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 ) {
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Picea\ControlStructure;
class ContinueToken implements ControlStructure {
public string $token = "continue";
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
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;
}
}
} }
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Picea\ControlStructure;
class FunctionToken implements ControlStructure {
public array $token = [ "function", "endfunction", "return" ];
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
switch($token) {
case "function":
$context->functions++;
return $this->printFunction($context, $arguments);
case "return":
if ( empty($context->functions) ) {
throw new \RuntimeException("A function return tag {% return %} was found without an opening {% function ... %} tag");
}
return $this->printReturn($context, $arguments);
case "endfunction":
if ( empty($context->functions) ) {
throw new \RuntimeException("A function closing tag {% endfunction %} was found without an opening {% function ... %} tag");
}
$context->functions--;
return $this->printEndFunction($context);
}
}
protected function printFunction($context, ?string $arguments) : string
{
return "<?php function $arguments { ?>";
}
protected function printReturn($context, ?string $arguments) : string
{
return "<?php return $arguments; ?>";
}
protected function printEndFunction($context) : string
{
return "<?php } ?>";
}
}
+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) {
}
}
+31 -7
View File
@@ -6,7 +6,17 @@ 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);
@@ -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 ? "\$___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);
} }
+28 -11
View File
@@ -6,33 +6,50 @@ use Picea\Compiler\Context;
class JsonExtension implements Extension, FunctionExtension { class JsonExtension implements Extension, FunctionExtension {
public array $token = [ "json", "json.pretty" ]; public array $token = [ "json" ];
public int $flags = JSON_HEX_TAG | \JSON_HEX_APOS | \JSON_HEX_QUOT | \JSON_THROW_ON_ERROR; 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":
return "<?php echo htmlentities(json_encode($arguments, {$this->flags}), ENT_QUOTES, 'UTF-8') ?>";
} }
return "<?php echo json_encode($arguments, $flag) ?>"; if ( in_array('html', $opt) ) {
return "<?php echo htmlentities(json_encode($arguments, $flag), ENT_QUOTES, 'UTF-8') ?>";
}
$cls = static::class;
return "<?php echo json_encode(\\$cls::utf8($arguments), $flag) ?>";
} }
public function exportFunctions(): array public function exportFunctions(): array
{ {
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);
}, },
]; ];
} }
public static function utf8($src) {
if (is_array($src)) {
foreach ($src as $key => $value) {
$src[$key] = static::utf8($value);
}
}
elseif (is_string($src)) {
return mb_convert_encoding($src, "UTF-8", "UTF-8");
}
return $src;
}
} }
+48 -5
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", "_", "language.set" ]; public array $tokens = [ "lang", "_", "language" ];
public string $currentLanguage = ""; public string $currentLanguage = "";
@@ -24,19 +25,44 @@ 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 \$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments) ?>";
}
return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>";
case "_": case "_":
if ( in_array('raw', $opt) ) {
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>"; return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>";
} }
return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>";
}
return ""; return "";
} }
@@ -53,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;
}
};
}
} }
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Picea\Extension;
use Picea\Compiler\Context;
class MoneyExtension implements Extension, FunctionExtension {
public string $token = "money";
public string $title = "";
public string $locale;
public \NumberFormatter $formatter;
public function __construct() {
$this->locale = explode('.', \Locale::getDefault())[0];
$this->formatter = new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY);
}
public function exportFunctions(): array
{
return [
"money" => [ $this, 'money' ]
];
}
public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) {
return "<?php echo money($arguments) ?>";
}
public function money(float $money, ? string $currency = null) /* : string|false */
{
$this->formatter->setTextAttribute(\NumberFormatter::CURRENCY_CODE, 'CAD');
$this->formatter->setPattern( str_replace('¤#','¤ #', $this->formatter->getPattern() ) );
return $this->formatter->formatCurrency($money, $currency ?? $this->formatter->getTextAttribute(\NumberFormatter::CURRENCY_CODE));
}
}
+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 ?>";
} }
} }
+14 -5
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
{ {
$opt = $context->tokenOptions($token);
switch($token) { switch($token) {
case 'echo':
case 'print': case 'print':
case "print.safe": return "<?php echo htmlspecialchars((string) $arguments, {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
return "<?php echo htmlspecialchars(sprintf($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) ?>";
} }
} }
+13 -11
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);
}
public function register(Context $context) : void
{ {
$context->pushFunction("title", [ $this, 'handleTitle' ]); return [
"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) ?>"; return <<<PHP
<?php
if ( null !== \$title = title($arguments) ) {
echo \$title;
}
?>
PHP;
} }
public function handleTitle(? string $set = null, ...$arguments) : ? string public function handleTitle(? string $set = null, ...$arguments) : ? string
+77 -29
View File
@@ -4,7 +4,15 @@ 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
~(?<!href=['"])https?://[\w/._\-&?]*(?!</a>)(?=[^\w/._\-&])~s
PATTERN;
public const URLIZE_PATTERN_EMAIL = <<<PATTERN
/(?:[a-z0-9!#$%&'*+\\/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+\\/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])/
PATTERN;
protected string $urlBase; protected string $urlBase;
@@ -14,16 +22,17 @@ class UrlExtension implements Extension {
protected array $routesTarget; protected array $routesTarget;
public array $tokens = [ "url" , "route", "asset", "url.current", "url.parameters" ]; 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) ?>";
@@ -32,24 +41,32 @@ class UrlExtension implements Extension {
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": case "slug":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->currentUrl($arguments) ?>"; return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->slug($arguments) ?>";
case "url.parameters":
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->setUrlParameters($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' ],
"route" => [ $this, 'buildRouteUrl' ],
"slug" => [ $this, 'slug' ],
"urlize" => [ $this, 'urlize' ]
];
} }
public function getRouteList(bool $full = false) : array public function getRouteList(bool $full = false) : array
@@ -62,25 +79,36 @@ class UrlExtension implements Extension {
return $url . ( $parameters ? "?" . http_build_query($parameters) : "" ); return $url . ( $parameters ? "?" . http_build_query($parameters) : "" );
} }
public function urlize(string $string) : string
{
# Normal URL patterns
$string = preg_replace(static::URLIZE_PATTERN_URL, '<a href="$0" target="_blank" title="$0">$0</a>', $string);
# Email patterns
$string = preg_replace(static::URLIZE_PATTERN_EMAIL, '<a href="mailto:$0" title="$0">$0</a>', $string);
return $string;
}
public function currentUrl(array $parameters = []) : string public function currentUrl(array $parameters = []) : string
{ {
return $this->buildUrl($this->uri(), $parameters); return $this->buildUrl($this->uri(), $parameters);
} }
public function buildUrl(string $uri = "", array $parameters = []) : string public function buildUrl(string $uri = "", array $parameters = [], bool $appendVersion = false) : string
{ {
return $this->setUrlParameters($this->url() . "/" . ltrim($uri, "/"), $parameters); return $this->setUrlParameters($this->url() . "/" . ltrim($uri, "/"), $appendVersion ? array_replace([ 'v' => $this->assetToken ], $parameters) : $parameters);
} }
public function buildAssetUrl(string $uri, array $parameters = []) : string public function buildAssetUrl(string $uri, array $parameters = [], bool $appendVersion = true) : string
{ {
return $this->buildUrl($uri, array_replace([ 'v' => $this->assetToken ], $parameters)); return $this->buildUrl($uri, $parameters, $appendVersion);
} }
public function buildRouteUrl(string $name, array $parameters = []) : string public function buildRouteUrl(string $name, array $parameters = [], bool $appendVersion = false) : string
{ {
if ( false !== ( $route = $this->routes[$name] ?? false ) ) { if ( false !== ( $route = $this->routes[$name] ?? false ) ) {
return $this->buildUrl($this->prepareRoute($route['route'], $parameters), $parameters); return $this->buildUrl($this->prepareRoute($route['route'], $parameters), $parameters, $appendVersion);
} }
$routeList = json_encode($this->routes, \JSON_PRETTY_PRINT); $routeList = json_encode($this->routes, \JSON_PRETTY_PRINT);
@@ -96,6 +124,16 @@ class UrlExtension implements Extension {
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
{
$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
{ {
$this->routes[$name] = [ $this->routes[$name] = [
@@ -159,18 +197,19 @@ class UrlExtension implements Extension {
protected function isHttps() : bool protected function isHttps() : bool
{ {
$header = in_array('https', array_map("strtolower", [ $https = in_array('https', array_map("strtolower", [
$_SERVER['REQUEST_SCHEME'] ?? "",
$_SERVER['X-Url-Scheme'] ?? "", $_SERVER['X-Url-Scheme'] ?? "",
$_SERVER['Front-End-Https'] ?? "", $_SERVER['Front-End-Https'] ?? "",
$_SERVER['X-Forwarded-Ssl'] ?? "",
$_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_PROTOCOL'] ?? "",
])) || isset($_SERVER['HTTP_X_ARR_SSL']);
return $header return $https
|| ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) || ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) || ( "443" === ( $_SERVER['HTTP_X_FORWARDED_PORT'] ?? "" ) )
|| ( "https" === ( $_SERVER['REQUEST_SCHEME'] ?? "http") ) || ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? $_SERVER['HTTP_X_FORWARDED_SSL'] ?? $_SERVER['X-Forwarded-Ssl'] ?? "off")) );
|| ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? "off")) );
} }
protected function prepareRoute(string $route, array &$arguments) protected function prepareRoute(string $route, array &$arguments)
@@ -204,7 +243,16 @@ class UrlExtension implements Extension {
$route = str_replace(array_keys($search), array_values($search), $route); $route = str_replace(array_keys($search), array_values($search), $route);
} }
return $route; /*
* @TODO - must also take into account that recursivity is possible here [/test[/another[/still]]],
* so the regex must be adjusted (or simply using another method could also be a possiblity)
* while(strpos($route, '[') !== false && strpos($route, ']') !== false ) {
if ( preg_match_all('~[(.*?)]~si', $route, $matches, PREG_SET_ORDER) ) {
} }
}*/
$route = trim(str_replace([ '[', ']' ], '', $route), '/');
return $route;
}
} }
+49 -5
View File
@@ -2,6 +2,8 @@
namespace Picea; namespace Picea;
use RecursiveIteratorIterator, RecursiveDirectoryIterator;
class FileFetcher class FileFetcher
{ {
protected array $folderList = []; protected array $folderList = [];
@@ -12,13 +14,13 @@ class FileFetcher
public function __construct(?array $folderList = null) { public function __construct(?array $folderList = null) {
if ( $folderList !== null ) { if ( $folderList !== null ) {
$this->folderList = $folderList; $this->addFolders($folderList);
} }
} }
public function addFolder(string $folder, int $order = 100) : void public function addFolder(string $folder, int $order = 100) : void
{ {
$folder = rtrim($folder, DIRECTORY_SEPARATOR); $folder = $this->normalizeFolder($folder);
$this->folderList[$folder] = [ $this->folderList[$folder] = [
'path' => $folder, 'path' => $folder,
@@ -28,7 +30,14 @@ class FileFetcher
public function addFolders(array $folderList) : void public function addFolders(array $folderList) : void
{ {
$this->folderList = array_replace($this->folderList, $folderList); foreach($folderList as $folder) {
$this->addFolder($folder['path'], $folder['order']);
}
}
protected function normalizeFolder(string $path) : string
{
return rtrim($path, '/\\');
} }
public function folderList(?array $set = null) : ?array public function folderList(?array $set = null) : ?array
@@ -40,21 +49,31 @@ class FileFetcher
{ {
usort($this->folderList, fn($a, $b) => $a['order'] <=> $b['order']); usort($this->folderList, fn($a, $b) => $a['order'] <=> $b['order']);
foreach($this->folderList as $folder) { foreach($this->folderList() as $folder) {
foreach($this->supportedExtensionList as $extension) { foreach($this->supportedExtensionList as $extension) {
$file = $folder['path'] . DIRECTORY_SEPARATOR . "$fileName.$extension"; $file = $folder['path'] . DIRECTORY_SEPARATOR . "$fileName.$extension";
$file = str_replace([ '\\', '/' ], DIRECTORY_SEPARATOR, $file);
if ( file_exists($file) ) { if ( file_exists($file) ) {
if ( is_dir($file) ) {
throw new \RuntimeException("Given view file `$fileName` is a folder, you must provide a valid filename.");
}
return $file; return $file;
} }
} }
} }
# Fallback on full-path # Fallback on full-path
foreach($this->folderList as $folder) { foreach($this->folderList() as $folder) {
$file = $folder['path'] . DIRECTORY_SEPARATOR . $fileName; $file = $folder['path'] . DIRECTORY_SEPARATOR . $fileName;
$file = str_replace([ '\\', '/' ], DIRECTORY_SEPARATOR, $file);
if ( file_exists($file) ) { if ( file_exists($file) ) {
if ( is_dir($file) ) {
throw new \RuntimeException("Given view file `$fileName` is a folder, you must provide a valid filename.");
}
return $file; return $file;
} }
} }
@@ -62,6 +81,30 @@ class FileFetcher
throw new \RuntimeException("Given view file `$fileName` can not be found within given folder list.."); throw new \RuntimeException("Given view file `$fileName` can not be found within given folder list..");
} }
public function getFileList() : array
{
usort($this->folderList, fn($a, $b) => $a['order'] <=> $b['order']);
$list = [];
foreach($this->folderList() as $folder) {
$path = $folder['path'] . "/";
$list[$path] = [];
if ( \file_exists($path) ) {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($iterator as $file) {
if ($file->isFile() && in_array($file->getExtension(), $this->supportedExtensionList)) {
$list[$path][] = $file;
}
}
}
}
return $list;
}
public function getFilePath(string $fileName) : string public function getFilePath(string $fileName) : string
{ {
return $this->findFile($fileName); return $this->findFile($fileName);
@@ -71,4 +114,5 @@ class FileFetcher
{ {
return file_get_contents($this->getFilePath($fileName)); return file_get_contents($this->getFilePath($fileName));
} }
} }
+3 -1
View File
@@ -28,7 +28,6 @@ class DefaultRegistrations implements LanguageRegistration
public function registerSyntax(Compiler $compiler) : void public function registerSyntax(Compiler $compiler) : void
{ {
$compiler->registerSyntax(new \Picea\Syntax\PhpTagToken());
$compiler->registerSyntax(new \Picea\Syntax\CommentToken()); $compiler->registerSyntax(new \Picea\Syntax\CommentToken());
$compiler->registerSyntax(new \Picea\Syntax\EchoRawToken()); $compiler->registerSyntax(new \Picea\Syntax\EchoRawToken());
$compiler->registerSyntax(new \Picea\Syntax\EchoSafeToken()); $compiler->registerSyntax(new \Picea\Syntax\EchoSafeToken());
@@ -45,12 +44,15 @@ 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\BlockToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\BlockToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\IncludeToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\IncludeToken());
$compiler->registerControlStructure(new \Picea\ControlStructure\ViewToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ViewToken());
+28 -10
View File
@@ -7,9 +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\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;
@@ -20,18 +22,19 @@ 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)
@@ -46,7 +49,7 @@ class Request implements Extension {
public function post(? string $variableName = null, $default = null) public function post(? string $variableName = null, $default = null)
{ {
return $variableName === null ? $this->request->getParsedBody() : static::arrayGet($this->request->getParsedBody(), $variableName) ?? $default; return $variableName === null ? $this->_post() : static::arrayGet($this->_post(), $variableName) ?? $default;
} }
public function request(? string $variableName = null, $default = null) public function request(? string $variableName = null, $default = null)
@@ -75,4 +78,19 @@ class Request implements Extension {
return null; return null;
} }
} }
protected function _post() : array
{
$post = $this->request->getParsedBody();
if ( ! $post ) {
$content = utf8_encode((string) $this->request->getBody());
if ( $content && ( $json = json_decode($content, true) ) ) {
$post = $json;
}
}
return $post ?: [];
}
} }
+19 -54
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,78 +65,47 @@ 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->gatherCompiledObject($viewPath, $variables, $proxy);
try { try {
return call_user_func($this->gatherTemplateObject($viewPath, $variables, $proxy)); return call_user_func($object);
} }
catch(\Throwable $ex) { catch(\Throwable $ex) {
# Temporary class for an experiment 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; throw $ex;
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 exit();
{
$content = null;
if ( is_array($content) && isset($content['classname'], $content['namespace'], $content['view'], $content['extends']) ) {
return $content;
}
return null;
}
};
}
} }
/** /**
* Method used by Block and View tokens * Method used by Block and View tokens
* @param object $proxy
* @param string $viewPath
* @param array $variables
* @return type
*/ */
public function inlineHtml(? object $proxy, string $viewPath, array $variables) { public function inlineHtml(? object $proxy, string $viewPath, array $variables) {
return $this->renderHtml($viewPath, $this->globalVariables + $variables, $proxy); return $this->renderHtml($viewPath, $this->globalVariables + $variables, $proxy);
} }
/**
* Allows block to be called from templates
*/
public function inlineBlock(? object $proxy, string $viewPath, ... $variables) { public function inlineBlock(? object $proxy, string $viewPath, ... $variables) {
return $this->renderHtml($viewPath, [ 'inlineVariables' => $this->globalVariables + $variables ], $proxy); return $this->renderHtml($viewPath, [ 'inlineVariables' => $variables, 'globalVariables' => $this->globalVariables ], $proxy);
} }
/**
* Push view content inline
*/
public function inlineContent(string $viewPath) { public function inlineContent(string $viewPath) {
return $this->fileFetcher->getFileContent($viewPath); return $this->fileFetcher->getFileContent($viewPath);
} }
/**
* Renders a compile context
*/
public function renderContext(Compiler\Context $context) : object public function renderContext(Compiler\Context $context) : object
{ {
if ( null === $object = $this->contextFromCache($context) ) { if ( null === $object = $this->contextFromCache($context) ) {
+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;");
+2 -2
View File
@@ -18,12 +18,12 @@ 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;");
return "<?php echo htmlspecialchars({$line}, {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; return "<?php echo htmlspecialchars((string) ({$line}), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>";
}, $sourceCode); }, $sourceCode);
} }
-18
View File
@@ -1,18 +0,0 @@
<?php
namespace Picea\Syntax;
class PhpTagToken implements Syntax {
public string $tokenOpen = "\{\?";
public string $tokenClose = "\?\}";
public function parse(/*\Picae\Compiler\Context*/ &$context, string &$sourceCode)
{
$sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) {
return "<?php {$matches[2]} ?>";
}, $sourceCode);
}
}
+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);
} }