Merge branch 'master' of https://git.mcnd.ca/mcndave/picea
This commit is contained in:
		
						commit
						af212b07ca
					
				
							
								
								
									
										47
									
								
								docs/00-intro.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								docs/00-intro.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| # Picea | ||||
| 
 | ||||
| Welcome to the official Picea documentation. | ||||
| 
 | ||||
| This library offers modern features from templating language, while simply generating normal PHP after compilation, such as view inheritance, definable blocks, 'or' token on unrunned looped, etc... | ||||
| 
 | ||||
| Picea uses the same delimiters that Twig uses, which are `{% %}`, `{{ }}` and `{# #}`. | ||||
| 
 | ||||
| The first `{% %}` is used for most **control structure** and **extensions**. | ||||
| 
 | ||||
| The `{{ }}` delimiter is used to **echo escaped content** in a page. (see *01-echoing*) | ||||
| 
 | ||||
| The `{# #}` is exclusively used as a **comment** enclosure. (see *01-comment*) | ||||
| 
 | ||||
| ## Quick start | ||||
| 
 | ||||
| Render a simple Picea view: | ||||
| 
 | ||||
| ```php | ||||
| $picea = new Picea\Picea(); | ||||
| $picea->renderHtml('view/path/hello_world'); | ||||
| ``` | ||||
| 
 | ||||
| And the view content could look like: | ||||
| 
 | ||||
| *path/hello_world* | ||||
| ```html | ||||
| <!DOCTYPE html> | ||||
| <html>  | ||||
|     <head> | ||||
|         <title>Picea's simple </title> | ||||
|     </head> | ||||
|     <body> | ||||
|         <nav> | ||||
|             {% foreach $navigation as $navItem %} | ||||
|                 <a href="{% route $navItem->route %}" title="{{ $navItem->description }}"> | ||||
|                     {{ $navItem->caption }} | ||||
|                 </a> | ||||
|             {% endforeach %} | ||||
|         </nav> | ||||
|      | ||||
|         {{ $someText }} | ||||
|     </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										26
									
								
								docs/01-comment.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								docs/01-comment.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| # Control structures - Comments | ||||
| 
 | ||||
| There is no single-line token to comment code within Picea, it's a tag working as such : | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {# This is how you comment in a Picea view #} | ||||
| 
 | ||||
| <div> | ||||
|     {# Is it hello or good bye ? not sure on this ! #} | ||||
|     <i>Good bye world !</i> | ||||
| </div> | ||||
| 
 | ||||
| {# This is a multi-line comment | ||||
|     <div>This part will not be rendered</div> | ||||
| #} | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render as such: | ||||
| 
 | ||||
| ```html | ||||
| <div> | ||||
|     <i>Good bye world !</i> | ||||
| </div> | ||||
| ``` | ||||
							
								
								
									
										73
									
								
								docs/01-echoing.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								docs/01-echoing.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| # Echoing text or data | ||||
| 
 | ||||
| There is two token groups that you can use to print content into a view, `echo` or `print`. | ||||
| 
 | ||||
| Escaped by default : `{% echo $argument %}` = `{{ $argument }}` = `{% print $argument %}` | ||||
| 
 | ||||
| Raw when needed: `{% echo.raw $argument %}` = `{{= $argument }}` = `{% print.raw $argument %}` | ||||
| 
 | ||||
| ## Outputing content to a view using `echo` / `{{ }}` / `print`, `echo.raw` / `{{= }}` / `print.raw` | ||||
| 
 | ||||
| Using `print` or `echo`, which are, by default, made safer by using PHP's native `htmlspecialchars`. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {{ "Hello World !" }} | ||||
| 
 | ||||
| {% echo "This is another way to output <safe> content" %} | ||||
| 
 | ||||
| {% print "This is the third way to output <safe> content" %} | ||||
| 
 | ||||
| <hr> | ||||
| 
 | ||||
| {% echo.raw $someHTML %} | ||||
| 
 | ||||
| {% print.raw $someOtherHTML %} | ||||
| 
 | ||||
| {{= $someMoreHTML }} | ||||
| ``` | ||||
| 
 | ||||
| **[PHP]** Would yield internally: | ||||
| 
 | ||||
| ```html | ||||
| <?php echo htmlspecialchars((string) "Hello World !", 3, 'UTF-8', true) ?> | ||||
| 
 | ||||
| <?php echo htmlspecialchars((string) "This is another way to output <safe> content", 3, 'UTF-8', true) ?> | ||||
| 
 | ||||
| <?php echo htmlspecialchars((string) "This is another way to output <safe> content", 3, 'UTF-8', true) ?> | ||||
| 
 | ||||
| <hr> | ||||
| 
 | ||||
| <?php echo $someHTML ?> | ||||
| 
 | ||||
| <?php echo $someOtherHTML ?> | ||||
| 
 | ||||
| <?php echo $someMoreHTML ?> | ||||
| ``` | ||||
| 
 | ||||
| ## Using string format variant `printf` / `printf.raw` | ||||
| 
 | ||||
| Those tokens represents the equivalent of the printf() function from PHP. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {% php  | ||||
|     $num = 5; | ||||
|     $location = 'tree'; | ||||
| %} | ||||
| <span>{% printf 'There are %d monkeys in the %s', $num, $location %}</span> | ||||
| <i>{% printf.raw 'There are %d monkeys in the %s', $num, $location %}</i> | ||||
| ``` | ||||
| 
 | ||||
| **[PHP]** Would render internally as : | ||||
| 
 | ||||
| ```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
									
								
								docs/02-control-structure-comparisons.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								docs/02-control-structure-comparisons.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| # Control structure - Comparisons | ||||
| 
 | ||||
| Most control structures used within PHP view templates are supported natively in Picea. | ||||
| 
 | ||||
| The goal for this project always has been to not loose original feature while removing some limitations with PHP's original templating syntax. | ||||
| 
 | ||||
| ## Comparison using `if` / `then` / `else` / `elseif` / `endif`  | ||||
| 
 | ||||
| Comparisons works the same way as using PHP's alternative syntax. | ||||
| 
 | ||||
| This is also how they are rendered in the compilation process.  | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {% php $search = 'Pack my box with five dozen liquor jugs' %} | ||||
| 
 | ||||
| {% if strpos($search, 'beer') !== false %} | ||||
|     <span>Found 'beer' into {{ $search }} !</span> | ||||
| {% elseif strpos($search, 'liquor') !== false %} | ||||
|     <span>Found 'liquor' into {{ $search }} !</span> | ||||
| {% else %} | ||||
|     <b>Neither 'beer' or 'liquor' were found in {{ $search }}</b> | ||||
| {% endif %} | ||||
| ``` | ||||
| 
 | ||||
| **[PHP]** Would yield: | ||||
| 
 | ||||
| ```php | ||||
| <?php $search = 'Pack my box with five dozen liquor jugs'; ?> | ||||
| 
 | ||||
| <?php if (strpos($search, 'beer') !== false): ?> | ||||
|     <span>Found 'beer' into <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?> !</span> | ||||
| <?php elseif (strpos($search, 'beer') !== false): ?> | ||||
|     <span>Found 'liquor' into <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?> !</span> | ||||
| <?php else: ?> | ||||
|     <b>Neither 'beer' or 'liquor' were found in <?php echo htmlspecialchars((string) $search, 3, 'UTF-8', true) ?></b> | ||||
| <?php endif ?> | ||||
| ``` | ||||
| 
 | ||||
| And then, the code would be runned through PHP's native `include` mechanic. | ||||
| 
 | ||||
| ## Comparison using `switch` / `case` / `break` / `endswitch` | ||||
| 
 | ||||
| Using switches within HTML in plain PHP can be quite cumbersome because of it's limitation | ||||
| disallowing any output (including whitespace) between it's statement. | ||||
| 
 | ||||
| Picea will allow some more complex (and readable) switches within your views. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```php | ||||
| {% php $selected = random_int(0,5) %} | ||||
| 
 | ||||
| {% switch $selected %} | ||||
| 
 | ||||
| {% case 1 %} | ||||
|     {{ "One person selected" }} | ||||
| {% break %} | ||||
| 
 | ||||
| {% case 2 %} | ||||
| {% default %} | ||||
|     {{ "Multiple person ($selected) selected" }} | ||||
| {% break %} | ||||
| 
 | ||||
| {% endswitch %} | ||||
| ``` | ||||
| 
 | ||||
| **[PHP]** Would render as such: | ||||
| 
 | ||||
| ```php | ||||
| <?php $selected = random_int(0,5) ?> | ||||
| 
 | ||||
| <?php switch($selected): | ||||
| 
 | ||||
| case 1: ?> | ||||
|     <?php echo htmlspecialchars((string) "One person selected", 3, 'UTF-8', true) ?> | ||||
| <?php break; ?> | ||||
| 
 | ||||
| <?php case 2: ?> | ||||
| <?php default: ?> | ||||
|     <?php echo htmlspecialchars((string) "Multiple person ($selected) selected", 3, 'UTF-8', true) ?> | ||||
| <?php break; ?> | ||||
| 
 | ||||
| <?php endswitch; ?> | ||||
| ``` | ||||
							
								
								
									
										122
									
								
								docs/02-control-structure-extends-section.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								docs/02-control-structure-extends-section.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| # Control structure - `extends` / `section` | ||||
| 
 | ||||
| A nice feature of most robust templating engine is the ability to inherit from other view. | ||||
| 
 | ||||
| Picea follows a similar train of tought, since it's also possible create a chain of inheritance  | ||||
| using `extends` which have definable parts you can declare using `section`.  | ||||
| 
 | ||||
| ## Basic `extends` (string $path) | ||||
| 
 | ||||
| You must provide a valid `$path` from which the template will be inherited.  | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| *path/base/layout.phtml* | ||||
| ```html | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|     <head> | ||||
|         {% section "head" %} | ||||
|             <meta charset="utf-8"> | ||||
|             <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|             <title>{{ title() }} - AnExampleApp</title> | ||||
|         {% section %} | ||||
|     </head> | ||||
|     <body> | ||||
|         <header id="header""> | ||||
|             {% section "header" %}{% view "path/base/navigation" %}{% endsection %} | ||||
|         </header> | ||||
|         <main id="main">{% section "main" %}{% endsection %}</main> | ||||
|         <footer id="footer"> | ||||
|             {% section "footer" %} | ||||
|                 © Copyright {{ date('Y') }} | ||||
|             {% endsection %} | ||||
|         </footer> | ||||
|     </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| *path/home.phtml* | ||||
| ```html | ||||
| {% extends "path/base/layout" %} | ||||
| 
 | ||||
| {% title "Home page" %} | ||||
| 
 | ||||
| {% section "main" %} | ||||
|     <h1>Welcome !</h1> | ||||
|     {# @TODO integrate our new blog engine below ! #} | ||||
|     <article> | ||||
|         This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! | ||||
|     </article> | ||||
| {% endsection %} | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render as such : | ||||
| ```html | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|     <head> | ||||
|         <meta charset="utf-8"> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|         <title>Home page - AnExampleApp</title> | ||||
|     </head> | ||||
|     <body> | ||||
|         <header id="header""> | ||||
|             <nav> | ||||
|                 <a href="#home">Home</a> | ||||
|                 <a href="#login">Login</a> | ||||
|             </nav> | ||||
|         </header> | ||||
|         <main id="main"> | ||||
|             <h1>Welcome !</h1> | ||||
|              | ||||
|             <article> | ||||
|                 This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! | ||||
|             </article> | ||||
|         </main> | ||||
|         <footer id="footer"> | ||||
|             © Copyright 2022 | ||||
|         </footer> | ||||
|     </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| ### Inherit an already extended view | ||||
| 
 | ||||
| We could use the previous file `path/home` and generate, let's say, the same page, but without a navigation menu. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| *path/home-navless.phtml* | ||||
| ```html | ||||
| {% extends "path/home" %} | ||||
| 
 | ||||
| {% section "header" %}{% endsection %} | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render as such : | ||||
| ```html | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|     <head> | ||||
|         <meta charset="utf-8"> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|         <title>Home page - AnExampleApp</title> | ||||
|     </head> | ||||
|     <body> | ||||
|         <header id="header""></header> | ||||
|         <main id="main"> | ||||
|             <h1>Welcome !</h1> | ||||
|              | ||||
|             <article> | ||||
|                 This is our new blog ! We hope you are gonna enjoy your stay on our new platform ! | ||||
|             </article> | ||||
|         </main> | ||||
|         <footer id="footer"> | ||||
|             © Copyright 2022 | ||||
|         </footer> | ||||
|     </body> | ||||
| </html> | ||||
| ``` | ||||
| 
 | ||||
| Notice that the `<header>` tag is now empty, since we've redeclared it in our navless view. | ||||
							
								
								
									
										42
									
								
								docs/02-control-structure-function.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								docs/02-control-structure-function.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| # Control structure - `function` | ||||
| 
 | ||||
| Sometimes comes a need to have a small subset of code which is gonna be used twice (or more) | ||||
| in the same view. Instead of simply duplicating the code (with everything that can then go wrong if you ever need | ||||
| to play with it later), you could create a `function`. | ||||
| 
 | ||||
| Functions are declared exactly like vanilla PHP. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| 
 | ||||
| {% use Psr\Http\Message\ServerRequestInterface %} | ||||
| 
 | ||||
| {% title "My generic title" %} | ||||
| 
 | ||||
| {% function printCustomTitle(ServerRequestInterface $request) : bool %} | ||||
|     {% if $request->getAttribute('lean.route')->name === 'home' %} | ||||
|         <h4 class="title">This is a custom title !</h4> | ||||
| 
 | ||||
|         {% return true %} | ||||
|     {% endif %} | ||||
| 
 | ||||
|     {% return false %} | ||||
| {% endfunction %} | ||||
| 
 | ||||
| {% if ! printCustomTitle($request) %} | ||||
|     <h1>{{ title() }}</h1> | ||||
| {% endif %} | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would yield: | ||||
| 
 | ||||
| *page **is** 'home'* | ||||
| ```php | ||||
| <h4>This is a custom title !</h4> | ||||
| ``` | ||||
| 
 | ||||
| *page **is not** 'home'* | ||||
| ```php | ||||
| <h4>My generic title</h4> | ||||
| ``` | ||||
							
								
								
									
										193
									
								
								docs/02-control-structure-loops.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								docs/02-control-structure-loops.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,193 @@ | ||||
| # Control structure - Loops | ||||
| 
 | ||||
| Picea's loop works the same as PHP original alternative syntax. | ||||
| 
 | ||||
| There is, however, some minors improvments and a new `{% or %}` clause working much like the `{% else %}` clause in a comparison.   | ||||
| 
 | ||||
| ## Loop using `for` / `or` / `continue` / `break` / `endfor` | ||||
| 
 | ||||
| The simplest of sequence loop, `for` / `endfor` simply iterate over a given counter (or whatever your needs).  | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {% php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve' ], 0, random_int(0, 4)) %} | ||||
| 
 | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     {% for $i = 0; $i < count($users); $i++ %} | ||||
|         {% if $users[$i] === 'Steve' %} | ||||
|             {# Will leave the loop if user's name is Steve #} | ||||
|             {% break %} | ||||
|         {% else %} | ||||
|             <li>{{ $users[$i] }}</li> | ||||
|         {% endif %} | ||||
|     {% or %} | ||||
|         <li class="empty">Given user list was empty</li> | ||||
|     {% endfor %} | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[PHP]** Would compile like : | ||||
| 
 | ||||
| ```php | ||||
| <?php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve' ], 0, random_int(0, 4)) ?> | ||||
| 
 | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <?php for($i = 0; $i < count($users); $i++): ?><?php $for_a1kro2k2o = true; | ||||
|         <?php if ($users[$i] === 'Steve'): ?> | ||||
|             <?php break ?> | ||||
|         <?php else: ?> | ||||
|             <li><?php echo htmlspecialchars((string) $users[$i], 3, 'UTF-8', true) ?></li> | ||||
|         <?php endif ?> | ||||
|     <?php endfor ?><?php if (empty($for_a1kro2k2o)): ?><li class="empty">Given user list was empty</li><?php endif ?> | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** And would render as such given random_int() returns a **3**: | ||||
| 
 | ||||
| ```html | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <li>Tom</li> | ||||
|     <li>Sam</li> | ||||
|     <li>Mario</li> | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Or would render as such given random_int() returns a **0**: | ||||
| 
 | ||||
| ```html | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <li class="empty">Tom</li> | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| ## Loop using `foreach` / `or` / `continue` / `break` /  `endforeach` | ||||
| 
 | ||||
| The more complex `foreach` / `endforeach` allows to iterate over keys and values of an array. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| ```html | ||||
| {# Generate a random list of 0 to 4 names #} | ||||
| {% php $users = array_slice([ 'Tom', 'Sam', 'Mario', 'Steve', 'Joan' ], 0, random_int(0, 5)) %} | ||||
| 
 | ||||
| <h1>Random User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     {% foreach $users as $index => $name %} | ||||
|         {% if $name === 'Steve' %} | ||||
|             {# We skip Steve, but allows Joan #} | ||||
|             {% continue %} | ||||
|         {% endif %} | ||||
|         <li>{{ $name }}</li> | ||||
|     {% or %} | ||||
|         <li class="empty">Given user list was empty</li> | ||||
|     {% endforeach %} | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Could render as such if prior random_int() returns '**2**': | ||||
| ```html | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <li>Tom</li> | ||||
|     <li>Sam</li> | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Could render as such given random_int() returns '**0**': | ||||
| ```html | ||||
| <h1>User list</h1> | ||||
| 
 | ||||
| <ul></ul> | ||||
| ``` | ||||
| 
 | ||||
| ## Loop using `while` / `or` / `continue` / `break` / `endwhile` | ||||
| 
 | ||||
| This syntax allows to loop on a computed iteration count. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {% php $fruits = [ 'apple', 'pear', 'banana', 'tomato' ] %} | ||||
| 
 | ||||
| <h1>Grocery list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     {% while $item = array_pop($fruits) %} | ||||
|         <li>{{ ucfirst($item) }}</li>  | ||||
|     {% or %} | ||||
|         <li class="empty">We should never see this, since the list is always populated !</li> | ||||
|     {% endwhile %} | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render as such: | ||||
| 
 | ||||
| ```html | ||||
| <h1>Grocery list</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <li>Apple</li> | ||||
|     <li>Pear</li> | ||||
|     <li>Banana</li> | ||||
|     <li>Tomato</li> | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| ## Loop using `do` / `continue` / `break` / `while` | ||||
| 
 | ||||
| This syntax do not have an alternative syntax in vanilla PHP.  | ||||
| 
 | ||||
| Picea allows for this unusual syntax to be used the same way you would with any other control structure. | ||||
| 
 | ||||
| There is, however, no support for the `or` clause for this structure since it will iterate at least once. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| <h1>Random number generator</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     {% do %} | ||||
|         {% php $number = random_int(1, 25); %} | ||||
|          | ||||
|         {% if $number === 10 %} | ||||
|             {# For a reason I'm not entirely sure why, 10 must not be displayed ! #} | ||||
|             {% continue %} | ||||
|         {% endif %} | ||||
|      | ||||
|         <li>{{ str_pad($number, 2, '0' ,STR_PAD_LEFT) }}</li>  | ||||
|     {% while $number !== 15 %}   {# Loop stops whenever $number is equal to 15 #} | ||||
| </ul> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Could render as such: | ||||
| 
 | ||||
| ```html | ||||
| <h1>Random number generator</h1> | ||||
| 
 | ||||
| <ul> | ||||
|     <li>02</li> | ||||
|     <li>12</li> | ||||
|     <li>07</li> | ||||
|     <li>13</li> | ||||
|     <li>04</li> | ||||
|     <li>07</li> | ||||
|     <li>01</li> | ||||
|     <li>16</li> | ||||
|     <li>14</li> | ||||
|     <li>24</li> | ||||
|     <li>14</li> | ||||
|     <li>01</li> | ||||
|     <li>15</li> | ||||
| </ul> | ||||
| ``` | ||||
							
								
								
									
										34
									
								
								docs/02-control-structure-use.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								docs/02-control-structure-use.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| # Control structure - `Use` | ||||
| 
 | ||||
| While working with inside a template, it might be needed that you reference some other namespaces. | ||||
| 
 | ||||
| The use clause works exaclty like PHP's vanilla top of the page `use`. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| ```html | ||||
| {% use AnyVendor\My\Super\Randomizer %} | ||||
| 
 | ||||
| {# One chance out of ten to end this loop %} | ||||
| {% while Randomizer::run(1, 10) === 10 %} | ||||
|     {% if Randomizer::run(1, 100) < 50 %}  | ||||
|         <span>Lower than 50 !</span> | ||||
|     {% else %} | ||||
|         <strong>Greater than 50 !</strong> | ||||
|     {% endif %} | ||||
| {% endwhile %} | ||||
| 
 | ||||
| <h4>DONE !</h4> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Could render as such output: | ||||
| 
 | ||||
| ```html | ||||
| <span>Lower than 50 !</span> | ||||
| <span>Greater than 50 !</span> | ||||
| <span>Lower than 50 !</span> | ||||
| <span>Greater than 50 !</span> | ||||
| <span>Lower than 50 !</span> | ||||
| <span>Lower than 50 !</span> | ||||
| <h4>DONE !</h4> | ||||
| ``` | ||||
							
								
								
									
										112
									
								
								docs/02-control-structure-view-include-block.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								docs/02-control-structure-view-include-block.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| # Control structure - View (or `include`) / Block | ||||
| 
 | ||||
| There is three ways to include other views into your current view `{% view %} / {% include %}` or  | ||||
| using `{% block %}{% endblock %}`. | ||||
| 
 | ||||
| The order of loading depends on the order given within your directory configuration; the first file found from it  | ||||
| will be used.  | ||||
| 
 | ||||
| ## Inline `view` (string $path, array|null $arguments = null) | ||||
| 
 | ||||
| You must provide a valid `$path` from which the template will be loaded. If no `$arguments` are provided, defined  | ||||
| variables from the caller's scope will be given (from `get_defined_vars()`). | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| *path/base/nav.phtml* | ||||
| ```html | ||||
| <nav> | ||||
|     {% foreach $navigation as $index => $navItem %} | ||||
|         {% view "path/base/nav-item", [ 'item' => $navItem, 'index' => $index] %} | ||||
|     {% endforeach %} | ||||
| </nav> | ||||
| ``` | ||||
| 
 | ||||
| *path/base/nav-item.phtml* | ||||
| ```html | ||||
| <a href="{{ $item->anchor }}" tabindex="{{ $index }}">{{ $item->name }}"</a> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Could render such as : | ||||
| ```html | ||||
| <nav> | ||||
|     <a href="#top">Top</a> | ||||
|     <a href="#bottom">Bottom</a> | ||||
|     <a href="#left">Left</a> | ||||
|     <a href="#right">Right</a> | ||||
| </nav> | ||||
| ``` | ||||
| 
 | ||||
| ## `include` (string $path) content from external file | ||||
| 
 | ||||
| Whenever you need to `include` a raw file from one of your view directories | ||||
| 
 | ||||
| ## Reusable `block` (string $path, ...$arguments) / `endblock` | ||||
| 
 | ||||
| A better way to achieve this behaviour could be to create a `block` which you can define as needed. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| *path/base/nav.phtml* | ||||
| ```html | ||||
| <nav> | ||||
|     {% foreach $navigation as $index => $navItem %} | ||||
|         {% block "path/base/nav-item-block", $navItem->name, $navItem->anchor, $index %}{% endblock %} | ||||
|     {% endforeach %} | ||||
| </nav> | ||||
| ``` | ||||
| 
 | ||||
| *path/base/nav-item-block.phtml* | ||||
| ```html | ||||
| {% arguments string $name, string $anchor, int $index = 0 %} | ||||
| 
 | ||||
| <a href="{{ $anchor }}" tabindex="{{ $index }}">{{ $name }}"</a> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render the same as the `view` example : | ||||
| ```html | ||||
| <nav> | ||||
|     <a href="#top">Top</a> | ||||
|     <a href="#bottom">Bottom</a> | ||||
|     <a href="#left">Left</a> | ||||
|     <a href="#right">Right</a> | ||||
| </nav> | ||||
| ``` | ||||
| 
 | ||||
| ### Extending a `block` using `define` and `slot` | ||||
| 
 | ||||
| You might need to define some custom content inside of a `block`. | ||||
| 
 | ||||
| You can do so by using `define` and `slot`. | ||||
| 
 | ||||
| **[PICEA]** So, using this code: | ||||
| 
 | ||||
| *path/base/nav.phtml* | ||||
| ```html | ||||
| <nav> | ||||
|     {% foreach $navigation as $index => $navItem %} | ||||
|         {% block "path/base/nav-item-block", $navItem->name, $navItem->anchor, $index %} | ||||
|             {% slot "attributes" %} class="nav-item" href="{{ $anchor }}" {% endslot %} | ||||
|         {% endblock %} | ||||
|     {% endforeach %} | ||||
| </nav> | ||||
| ``` | ||||
| 
 | ||||
| *path/base/nav-item-block.phtml* | ||||
| ```html | ||||
| {% arguments string $name, string $anchor, int $index = 0 %} | ||||
| 
 | ||||
| {% define slot %} | ||||
| 
 | ||||
| <a {% slot "attributes" %}href="{{ $anchor }}" tabindex="{{ $index }}"{% endslot %}>{{ $name }}"</a> | ||||
| ``` | ||||
| 
 | ||||
| **[HTML]** Would render the same as the `view` example : | ||||
| ```html | ||||
| <nav> | ||||
|     <a href="#top">Top</a> | ||||
|     <a href="#bottom">Bottom</a> | ||||
|     <a href="#left">Left</a> | ||||
|     <a href="#right">Right</a> | ||||
| </nav> | ||||
| ``` | ||||
							
								
								
									
										0
									
								
								docs/03-method-request.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/03-method-request.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										4
									
								
								docs/10-extension-custom.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/10-extension-custom.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| # Extension - Creating an extension | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										4
									
								
								docs/10-extension-json.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docs/10-extension-json.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| # Extension - `json`, `json.pretty`, `json.html` | ||||
| 
 | ||||
| This extension  | ||||
| 
 | ||||
							
								
								
									
										0
									
								
								docs/10-extension-language.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-language.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								docs/10-extension-money.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-money.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								docs/10-extension-php.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-php.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								docs/10-extension-title.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/10-extension-title.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| <?php | ||||
							
								
								
									
										0
									
								
								docs/10-extension-url.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								docs/10-extension-url.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										82
									
								
								docs/99-dependency-injection.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								docs/99-dependency-injection.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| # Using Dependency Injection | ||||
| 
 | ||||
| Here's an excerpt from Lean's definition file 'template.php' which also loads Picea\Ui. | ||||
| 
 | ||||
| It also insert `Tell` inside the LanguageHandler which handles the `{% _ 'relative.lang.key' %}` and `{% lang 'my.full.lang.key' %}`. | ||||
| 
 | ||||
| ```php | ||||
| use function DI\autowire, DI\create, DI\get; | ||||
| 
 | ||||
| use Zend\Diactoros\Response\HtmlResponse; | ||||
| 
 | ||||
| use Picea\{ Picea, Caching\Cache, Caching\Opcache, Compiler, Compiler\Context, Compiler\BaseContext, FileFetcher, Language\DefaultRegistrations, Method\Request }; | ||||
| use Picea\Extension\{ LanguageHandler, LanguageExtension, TitleExtension, MoneyExtension, UrlExtension }; | ||||
| use Picea\Ui\{ Method, Ui }; | ||||
| 
 | ||||
| return [ | ||||
|     Picea::class => function($c) { | ||||
|         return new Picea($c->get(Context::class), $c->get(Cache::class), $c->get(Compiler::class), null, $c->get(FileFetcher::class), null, getenv("DEBUG")); | ||||
|     }, | ||||
| 
 | ||||
|     Context::class => function($c) { | ||||
|         return new BaseContext( $c->get(Lean\Lean::class)->getPiceaContext() ); | ||||
|     }, | ||||
| 
 | ||||
|     Compiler::class => function($c) { | ||||
|         return new Compiler(new class(array_merge([ | ||||
|             $c->get(LanguageExtension::class), | ||||
|             $c->get(TitleExtension::class), | ||||
|             $c->get(MoneyExtension::class), | ||||
|             $c->get(UrlExtension::class), | ||||
|             $c->get(Method\Form::class), | ||||
|             $c->get(Method\Pagination::class), | ||||
|             $c->get(Request::class), | ||||
|         ], class_exists(\Taxus\Picea\Extension::class) ? [ $c->get(\Taxus\Picea\Extension::class) ] : [], | ||||
|             array_map(fn($class) => $c->get($class), $c->get(Lean\Lean::class)->getPiceaExtensions() ))) extends DefaultRegistrations { | ||||
| 
 | ||||
|             public function registerAll(Compiler $compiler) : void | ||||
|             { | ||||
|                 parent::registerAll($compiler); | ||||
|                 ( new Ui() )->registerFormExtension($compiler); | ||||
|             } | ||||
| 
 | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     Request::class => autowire(Request::class), | ||||
| 
 | ||||
|     Method\Form::class => autowire(Method\Form::class), | ||||
| 
 | ||||
|     Method\Pagination::class => autowire(Method\Pagination::class), | ||||
| 
 | ||||
|     LanguageExtension::class => create(LanguageExtension::class)->constructor(get(LanguageHandler::class)), | ||||
| 
 | ||||
|     LanguageHandler::class => function($c) { | ||||
|         return new class( $c->get(Tell\I18n::class) ) implements LanguageHandler { | ||||
|             public Tell\I18n $tell; | ||||
| 
 | ||||
|             public function __construct(Tell\I18n $tell) { | ||||
|                 $this->tell = $tell; | ||||
|             } | ||||
| 
 | ||||
|             public function languageFromKey(string $key, array $variables = []) : array|string | ||||
|             { | ||||
|                 return $this->tell->fromKey($key, $variables) ?: ""; | ||||
|             } | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     TitleExtension::class => autowire(TitleExtension::class), | ||||
| 
 | ||||
|     MoneyExtension::class => autowire(MoneyExtension::class), | ||||
| 
 | ||||
|     UrlExtension::class => create(UrlExtension::class)->constructor(get(Context::class), getenv("URL_BASE"), get('git.commit')), | ||||
| 
 | ||||
|     Cache::class => create(Opcache::class)->constructor(getenv("CACHE_PATH"), get(Context::class)), | ||||
| 
 | ||||
|     FileFetcher::class => function($c) { | ||||
|         return new FileFetcher($c->get(Lean\Lean::class)->getViewPaths()); | ||||
|     }, | ||||
| ]; | ||||
| ``` | ||||
| 
 | ||||
| @ -4,7 +4,7 @@ namespace Picea; | ||||
| 
 | ||||
| class Builder | ||||
| { | ||||
|     const TEMPLATE_CLASSNAME_PREFIX = "PiceaTemplate_"; | ||||
|     public const TEMPLATE_CLASSNAME_PREFIX = "PiceaTemplate_"; | ||||
| 
 | ||||
|     protected string $templatePath = ""; | ||||
| 
 | ||||
|  | ||||
| @ -8,10 +8,13 @@ namespace %NAMESPACE%; | ||||
| if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { | ||||
| 
 | ||||
|     class %CLASSNAME% %EXTENDS% { | ||||
| 
 | ||||
|         public array $blockList = []; | ||||
| 
 | ||||
|         public array $sectionList = []; | ||||
| 
 | ||||
|         public array $sectionStack = []; | ||||
| 
 | ||||
|         public array $variableList = []; | ||||
| 
 | ||||
|         public ?object $thisProxy = null; | ||||
| @ -24,6 +27,8 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { | ||||
| 
 | ||||
|         public bool $renderingInsideSection = false; | ||||
| 
 | ||||
|         public int $depth = 0; | ||||
| 
 | ||||
|         public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) { | ||||
|             $this->picea = $picea; | ||||
|             $this->variableList = $variablesList; | ||||
| @ -37,15 +42,26 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { | ||||
| 
 | ||||
|         public function output(array $variablesList = []) : void | ||||
|         { | ||||
|             $__event = new \Picea\Builder\ClassTemplateEvent(); | ||||
| 
 | ||||
|             $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($___variables, \EXTR_OVERWRITE); | ||||
|                 ?>%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% | ||||
| 
 | ||||
|             $this->depth--; | ||||
| 
 | ||||
|             $this->rendering = false; | ||||
|         } | ||||
| 
 | ||||
| @ -85,6 +101,14 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { | ||||
| 
 | ||||
|         public static function getSourceLineFromException(int $sourceLine) : ? int | ||||
|         { | ||||
|             $selfSource = file_get_contents(__FILE__); | ||||
| 
 | ||||
|             foreach(explode("\n", $selfSource) as $line => $content) { | ||||
|                 if ( strpos($content, str_replace('$', '%', '/*$EXCEPTION_LINE_BASE$*/')) !== false ) { | ||||
|                     return $sourceLine - $line; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             $sourceFile = file_get_contents("%TEMPLATE%"); | ||||
| 
 | ||||
|             if ( $sourceFile ) { | ||||
|  | ||||
							
								
								
									
										8
									
								
								src/Builder/ClassTemplateEvent.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Builder/ClassTemplateEvent.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| <?php declare(strict_types=1); | ||||
| 
 | ||||
| namespace Picea\Builder; | ||||
| 
 | ||||
| class ClassTemplateEvent | ||||
| { | ||||
|     use \Picea\EventTrait; | ||||
| } | ||||
| @ -29,4 +29,9 @@ interface Cache { | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function save(Context $context) : bool; | ||||
| 
 | ||||
|     /** | ||||
|      * Purge path cache | ||||
|      */ | ||||
|     public function purge() : void; | ||||
| } | ||||
|  | ||||
| @ -2,8 +2,7 @@ | ||||
| 
 | ||||
| namespace Picea\Caching; | ||||
| 
 | ||||
| use Picea\Builder, | ||||
|     Picea\Compiler\Context; | ||||
| use Picea\ { Builder, Compiler\Context }; | ||||
| 
 | ||||
| class Opcache implements Cache { | ||||
| 
 | ||||
| @ -57,11 +56,7 @@ class Opcache implements Cache { | ||||
| 
 | ||||
|         if ( file_exists($path = $this->cachePath($viewPath)) ) { | ||||
|             $this->compiled[$viewPath] = include($path); | ||||
|              | ||||
|             # if ( $this->compiled[$viewPath]['extends'] ?? false ) {
 | ||||
|             #     $this->compiled($this->compiled[$viewPath]['extends']);
 | ||||
|             # }
 | ||||
|              | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|         | ||||
| @ -86,7 +81,7 @@ class Opcache implements Cache { | ||||
|         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 ])); | ||||
|     } | ||||
| @ -95,7 +90,7 @@ class Opcache implements Cache { | ||||
|         spl_autoload_register(function ($class) use ($namespace) { | ||||
|             $prefix = "$namespace\\"; | ||||
| 
 | ||||
|             $baseDir = $this->cachePath() . "/"; | ||||
|             $baseDir = $this->cachePath() . DIRECTORY_SEPARATOR; | ||||
|              | ||||
|             $len = strlen($prefix); | ||||
|              | ||||
| @ -103,11 +98,24 @@ class Opcache implements Cache { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             $file = $baseDir . str_replace('\\', '/', substr($class, $len)) . '.php'; | ||||
|             $file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $len)) . '.php'; | ||||
| 
 | ||||
|             if ( file_exists($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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -122,14 +122,19 @@ class Compiler | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function __toString() : string | ||||
|     { | ||||
|         return "WHATAFAK"; | ||||
|     } | ||||
| 
 | ||||
|     public function getExtensionFromToken(string $name) : Extension\Extension | ||||
|     { | ||||
|         if ( false === $this->extensionList[$name] ?? false ) { | ||||
|         if ( ! isset($this->extensionList[$name]) ) { | ||||
|             throw new \InvalidArgumentException(<<<MSG | ||||
|                 Requested extension from token `$name` could not be found from loaded Picea compiler's extension. | ||||
|             MSG); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         return $this->extensionList[$name]; | ||||
|     } | ||||
|      | ||||
|  | ||||
| @ -24,6 +24,8 @@ abstract class Context { | ||||
| 
 | ||||
|     public array $useStack = []; | ||||
| 
 | ||||
|     public int $functions = 0; | ||||
|      | ||||
|     public array $functionStack = []; | ||||
| 
 | ||||
|     public array $hooks = []; | ||||
|  | ||||
							
								
								
									
										47
									
								
								src/ControlStructure/AbstractLoop.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/ControlStructure/AbstractLoop.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| abstract class AbstractLoop implements ControlStructure { | ||||
| 
 | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "while": | ||||
|             case "foreach": | ||||
|                 $name = "$".uniqid("{$token}_"); | ||||
|                  | ||||
|                 $stack = array_filter($context->iterationStack ?? [], function($item) { | ||||
|                     return ! $item['or']; | ||||
|                 }); | ||||
|                  | ||||
|                 $count = count($stack); | ||||
| 
 | ||||
|                 if ( $count > 0 ) { | ||||
|                     $name .= "[" . end($stack)['uid'] . "]"; | ||||
|                 } | ||||
| 
 | ||||
|                 $context->iterationStack[] = [ | ||||
|                     'or' => false, | ||||
|                     'uid' => $name, | ||||
|                     'token' => "end{$token}", | ||||
|                 ]; | ||||
| 
 | ||||
|                 return "<?php $token ($arguments): $name = ( $name ?? 0 ) + 1; ?>"; | ||||
| 
 | ||||
|             case "endwhile": | ||||
|             case "endforeach": | ||||
|                 $last = end($context->iterationStack); | ||||
| 
 | ||||
|                 if ( $last['or'] === false ) { | ||||
|                     $output = "<?php $token; ?>"; | ||||
|                 } | ||||
|                 else { | ||||
|                     $output = "<?php endif; if ( isset({$last['uid']}) ) unset({$last['uid']}); ?>"; | ||||
|                 } | ||||
| 
 | ||||
|                 array_pop($context->iterationStack); | ||||
|                  | ||||
|                 return $output; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -6,7 +6,7 @@ class BlockToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "arguments", "block", "endblock", "define", "slot", "endslot", "using" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         static $slotDefinitions = []; | ||||
| 
 | ||||
|         switch($token) { | ||||
| @ -33,7 +33,7 @@ class BlockToken implements ControlStructure { | ||||
|                                 sprintf('A block awaiting arguments `%s` instead received `%s` with values `%s`', '$arguments', implode(', ', array_map('gettype', \$inlineVariables ?? [])), json_encode(\$inlineVariables)) | ||||
|                             ); | ||||
|                         } | ||||
|                     ?>
 | ||||
|                     /*%EXCEPTION_LINE_BASE%*/?>
 | ||||
|                 PHP; | ||||
|              | ||||
|             case "define": | ||||
| @ -49,7 +49,9 @@ class BlockToken implements ControlStructure { | ||||
|                 $def = end($slotDefinitions); | ||||
|                  | ||||
|                 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() ) { | ||||
|                     $slotName = eval("return $name;"); | ||||
|                     $def->currentSlot = $slotName; | ||||
| @ -62,7 +64,7 @@ class BlockToken implements ControlStructure { | ||||
|                     } | ||||
| 
 | ||||
|                     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; | ||||
|                 } | ||||
|                 else { | ||||
| @ -71,7 +73,7 @@ class BlockToken implements ControlStructure { | ||||
|                     } | ||||
|                      | ||||
|                     return <<<PHP | ||||
|                         <?php (\$___block ?? \$this)->slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea) { extract(\$___using, \EXTR_SKIP); ?>
 | ||||
|                         <?php (\$___block ?? \$this)->slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea $loops) { extract(\$___using, \EXTR_SKIP); ?>
 | ||||
|                     PHP; | ||||
|                 } | ||||
|                      | ||||
|  | ||||
| @ -6,7 +6,7 @@ class BreakToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "break"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         return "<?php break; ?>"; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ class CaseToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "case"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         $output = ""; | ||||
| 
 | ||||
|         if ( $context->switchStack ) { | ||||
|  | ||||
| @ -6,7 +6,7 @@ class ContinueToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "continue"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         return "<?php continue; ?>"; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -3,5 +3,5 @@ | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| interface ControlStructure { | ||||
|     public function parse(\Picae\Compiler\Context &$context, string $sourceCode, string $token); | ||||
|     public function parse(\Picea\Compiler\Context &$context, string $sourceCode, string $token); | ||||
| } | ||||
|  | ||||
| @ -6,7 +6,7 @@ class DefaultToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "default"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         $output = ""; | ||||
| 
 | ||||
|         if ( $context->switchStack ) { | ||||
|  | ||||
							
								
								
									
										28
									
								
								src/ControlStructure/EchoToken.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/ControlStructure/EchoToken.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| class EchoToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "echo", "echo.raw" ]; | ||||
| 
 | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "echo": | ||||
|                 return "<?php \\Picea\\ControlStructure\\EchoToken::echoSafe($arguments) ?>"; | ||||
| 
 | ||||
|             case "echo.raw": | ||||
|                 return "<?php \\Picea\\ControlStructure\\EchoToken::echoRaw($arguments) ?>"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static function echoRaw($arguments) : string | ||||
|     { | ||||
|         return "<?php echo $arguments ?>"; | ||||
|     } | ||||
| 
 | ||||
|     public static function echoSafe($arguments) : string | ||||
|     { | ||||
|         return "<?php echo $arguments ?>"; | ||||
|     } | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| class EndRawToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "endraw"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -6,7 +6,7 @@ class ExtendsToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "extends"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $path, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $path, string $token) { | ||||
|         # Triming string's quotes
 | ||||
|         $path = trim($path, "\"\' \t"); | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ class ForToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "for", "endfor" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "for": | ||||
|                 $uid = "$".uniqid("for_"); | ||||
|  | ||||
| @ -2,46 +2,8 @@ | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| class ForeachToken implements ControlStructure { | ||||
| use DI\Definition\Source\AnnotationBasedAutowiring; | ||||
| 
 | ||||
| class ForeachToken extends AbstractLoop { | ||||
|     public array $token = [ "foreach", "endforeach" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "foreach": | ||||
|                 $name = "$".uniqid("foreach_"); | ||||
|                  | ||||
|                 $stack = array_filter($context->iterationStack ?? [], function($item) { | ||||
|                     return ! $item['or']; | ||||
|                 }); | ||||
|                  | ||||
|                 $count = count($stack); | ||||
| 
 | ||||
|                 if ( $count > 0 ) { | ||||
|                     $name .= "[" . end($stack)['uid'] . "]"; | ||||
|                 } | ||||
| 
 | ||||
|                 $context->iterationStack[] = [ | ||||
|                     'or' => false, | ||||
|                     'uid' => $name, | ||||
|                     'token' => 'endforeach', | ||||
|                 ]; | ||||
| 
 | ||||
|                 return "<?php foreach ($arguments): $name = ( $name ?? 0 ) + 1; ; ?>"; | ||||
|                  | ||||
|             case "endforeach": | ||||
|                 $last = end($context->iterationStack); | ||||
| 
 | ||||
|                 if ( $last['or'] === false ) { | ||||
|                     $output = "<?php endforeach; ?>"; | ||||
|                 } | ||||
|                 else { | ||||
|                     $output = "<?php endif; unset({$last['uid']}) ?>"; | ||||
|                 } | ||||
| 
 | ||||
|                 array_pop($context->iterationStack); | ||||
|                  | ||||
|                 return $output; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -6,9 +6,11 @@ class FunctionToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "function", "endfunction", "return" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "function": | ||||
|                 $context->functions++; | ||||
| 
 | ||||
|                 return $this->printFunction($context, $arguments); | ||||
| 
 | ||||
|             case "return": | ||||
| @ -31,7 +33,6 @@ class FunctionToken implements ControlStructure { | ||||
| 
 | ||||
|     protected function printFunction($context, ?string $arguments) : string | ||||
|     { | ||||
|         $context->functions++; | ||||
|         return "<?php function $arguments { ?>"; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ class IfToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "if", "else", "elseif", "endif" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "if": | ||||
|                 return "<?php if ($arguments): ?>"; | ||||
|  | ||||
| @ -6,7 +6,7 @@ class IncludeToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "include"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|         return "<?php echo \$___class__template->picea->inlineContent($arguments); ?>"; | ||||
|     public function parse(\Picea\Compiler\Context &$context, ? string $viewPath, string $token) { | ||||
|         return "<?php echo \$___class__template->picea->inlineContent($viewPath); ?>"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -6,7 +6,7 @@ class NamespaceToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "namespace"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         $context->namespace = $arguments; | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
| @ -6,9 +6,9 @@ class OrToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "or"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         if ( empty($context->iterationStack) ) { | ||||
|             throw new \LogicException("Token `or` was used outside of iterator. Make sure your `for` or `foreach` declaration are properly made."); | ||||
|             throw new \LogicException("Token `or` was used outside of iterator. Make sure your `for`, `foreach`, `while`, `do/until` declaration are properly made."); | ||||
|         } | ||||
| 
 | ||||
|         $key = count( $context->iterationStack ) - 1; | ||||
|  | ||||
| @ -1,12 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| class RawToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "raw"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -6,7 +6,7 @@ class SectionToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "section", "endsection" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "section": | ||||
|                 return $this->printSection($context, $arguments); | ||||
| @ -46,14 +46,26 @@ class SectionToken implements ControlStructure { | ||||
|         $order = $options['order'] ?? "count(\$___class__template->sectionList[$name]['$action'])"; | ||||
| 
 | ||||
|         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) {". | ||||
|                           "extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); \$___class__template->sectionStack[] = '$name'; ?>"; | ||||
|                       "\$___class__template->sectionList[$name]['$action'][] = [ 
 | ||||
|                       '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 | ||||
|     {             | ||||
|         $section = array_pop($context->sections); | ||||
|         $build = $context->extendFrom ? "!empty(\$___class__template->sectionStack) && \$___class__template->renderSection({$section['name']});" : "\$___class__template->renderSection({$section['name']});"; | ||||
|         return "<?php array_pop(\$___class__template->sectionStack); }]; $build?>"; | ||||
|         $build = $context->extendFrom ? "!empty(\$___class__template->sectionStack) && \$___class__template->renderSection({$section['name']}, false);" : "\$___class__template->renderSection({$section['name']}, false);"; | ||||
| 
 | ||||
|         return <<<PHP | ||||
|             <?php  | ||||
|                 \$__event->eventExecute(\Picea\Event\Builder\ClassTemplateRenderSectionDone::class, {$section['name']}); | ||||
|                 array_pop(\$___class__template->sectionStack); }];  | ||||
|                 $build | ||||
|             ?>
 | ||||
|         PHP; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -6,7 +6,7 @@ class SwitchToken implements ControlStructure { | ||||
| 
 | ||||
|     public array $token = [ "switch", "case", "endswitch" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "switch": | ||||
|                 $context->switchStack[] = true; | ||||
|  | ||||
| @ -6,7 +6,7 @@ class UseToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "use"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         $context->useStack[] = $arguments; | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
| @ -6,7 +6,7 @@ class ViewToken implements ControlStructure { | ||||
| 
 | ||||
|     public string $token = "view"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         # The way this is ordered, if you provide a second arguments, being an array of variables, get_defined_vars() will not be pushed inside the view
 | ||||
|         return "<?php echo \$___class__template->picea->inlineHtml(\$this, $arguments, get_defined_vars()); ?>"; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										32
									
								
								src/ControlStructure/WhileToken.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/ControlStructure/WhileToken.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\ControlStructure; | ||||
| 
 | ||||
| class WhileToken extends AbstractLoop { | ||||
|     public array $token = [ "do", "while", "endwhile", ]; | ||||
| 
 | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         switch($token) { | ||||
|             case "do": | ||||
|                 $context->iterationStack[] = [ | ||||
|                     'or' => false, | ||||
|                     'token' => "do", | ||||
|                 ]; | ||||
| 
 | ||||
|                 return "<?php do { ?>"; | ||||
| 
 | ||||
|             case "while": | ||||
|                 if ( $context->iterationStack ?? false ) { | ||||
|                     if ( end($context->iterationStack)['token'] === 'do' ) { | ||||
|                         array_pop($context->iterationStack); | ||||
| 
 | ||||
|                         return "<?php } while($arguments); ?>"; | ||||
|                     } | ||||
|                 } | ||||
|         } | ||||
| 
 | ||||
|         return parent::parse($context, $arguments, $token); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										7
									
								
								src/Event/Builder/ClassTemplateOutputDone.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Event/Builder/ClassTemplateOutputDone.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\Event\Builder; | ||||
| 
 | ||||
| interface ClassTemplateOutputDone { | ||||
|     public function execute(string $name, array $variablesList) : mixed; | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/Event/Builder/ClassTemplateOutputing.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Event/Builder/ClassTemplateOutputing.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\Event\Builder; | ||||
| 
 | ||||
| interface ClassTemplateOutputing { | ||||
|     public function execute(string $name, array $variablesList) : mixed; | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/Event/Builder/ClassTemplateRenderSection.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Event/Builder/ClassTemplateRenderSection.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\Event\Builder; | ||||
| 
 | ||||
| interface ClassTemplateRenderSection { | ||||
|     public function execute(string $name) : mixed; | ||||
| } | ||||
							
								
								
									
										7
									
								
								src/Event/Builder/ClassTemplateRenderSectionDone.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Event/Builder/ClassTemplateRenderSectionDone.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\Event\Builder; | ||||
| 
 | ||||
| interface ClassTemplateRenderSectionDone { | ||||
|     public function execute(string $section) : mixed; | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/EventTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/EventTrait.php
									
									
									
									
									
										Normal 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										52
									
								
								src/Exception/RenderHtmlException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/Exception/RenderHtmlException.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Picea\Exception; | ||||
| 
 | ||||
| use Picea\Picea; | ||||
| 
 | ||||
| /** | ||||
|  *  Whenever an error occured rendering a view, this is the exception that will show up. | ||||
|  * | ||||
|  */ | ||||
| class RenderHtmlException extends \Exception | ||||
| { | ||||
|     protected Picea $picea; | ||||
| 
 | ||||
|     public function __construct(object $compiledObject, Picea $picea, string $message, int $code, \Throwable $previous) | ||||
|     { | ||||
|         parent::__construct($message, $code, $previous); | ||||
| 
 | ||||
|         $this->picea = $picea; | ||||
| 
 | ||||
|         $this->defineError($previous, $compiledObject); | ||||
|     } | ||||
| 
 | ||||
|     protected function defineError(\Throwable $previous, object $compiledObject) : void | ||||
|     { | ||||
|         $loadedTemplates = array_flip($this->picea->loadedTemplateFile); | ||||
| 
 | ||||
|         foreach($previous->getTrace() as $trace) { | ||||
|             if ( isset($trace['file'], $loadedTemplates[$trace['file']]) ) { | ||||
|                 $class = $loadedTemplates[ $trace['file'] ]; | ||||
| 
 | ||||
|                 $content = include($trace['file']); | ||||
| 
 | ||||
|                 $this->file = $content['view']; | ||||
|                 $this->line = $class::getSourceLineFromException($trace['line']); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected function getTemplateFile(string $filePath) : ? array | ||||
|     { | ||||
|         $content = null; | ||||
| 
 | ||||
|         if ( is_array($content) && isset($content['classname'], $content['namespace'], $content['view'], $content['extends']) ) { | ||||
|             return $content; | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| @ -1,129 +0,0 @@ | ||||
| <?php | ||||
| 
 | ||||
| 
 | ||||
| namespace Twig\Error; | ||||
| 
 | ||||
| use Twig\Source; | ||||
| use Twig\Template; | ||||
| 
 | ||||
| /** | ||||
|  * Twig base exception. | ||||
|  * | ||||
|  * This exception class and its children must only be used when | ||||
|  * an error occurs during the loading of a template, when a syntax error | ||||
|  * is detected in a template, or when rendering a template. Other | ||||
|  * errors must use regular PHP exception classes (like when the template | ||||
|  * cache directory is not writable for instance). | ||||
|  * | ||||
|  * To help debugging template issues, this class tracks the original template | ||||
|  * name and line where the error occurred. | ||||
|  * | ||||
|  * Whenever possible, you must set these information (original template name | ||||
|  * and line number) yourself by passing them to the constructor. If some or all | ||||
|  * these information are not available from where you throw the exception, then | ||||
|  * this class will guess them automatically (when the line number is set to -1 | ||||
|  * and/or the name is set to null). As this is a costly operation, this | ||||
|  * can be disabled by passing false for both the name and the line number | ||||
|  * when creating a new instance of this class. | ||||
|  * | ||||
|  * @author Fabien Potencier <fabien@symfony.com> | ||||
|  */ | ||||
| class RenderingError extends \Exception | ||||
| { | ||||
|     /** | ||||
|      * Constructor. | ||||
|      * | ||||
|      * By default, automatic guessing is enabled. | ||||
|      * | ||||
|      * @param string      $message The error message | ||||
|      * @param int         $lineno  The template line where the error occurred | ||||
|      * @param Source|null $source  The source context where the error occurred | ||||
|      */ | ||||
|     public function __construct(string $message, Source $source = null, \Exception $previous = null) | ||||
|     { | ||||
|         parent::__construct('', 0, $previous); | ||||
| 
 | ||||
|         if (null === $source) { | ||||
|             $name = null; | ||||
|         } else { | ||||
|             $name = $source->getName(); | ||||
|             $this->sourceCode = $source->getCode(); | ||||
|             $this->sourcePath = $source->getPath(); | ||||
|         } | ||||
| 
 | ||||
|         $this->lineno = $lineno; | ||||
|         $this->name = $name; | ||||
|         $this->rawMessage = $message; | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static function generateFromCompiledClass(\Throwable $exception) : self | ||||
|     { | ||||
| 
 | ||||
| 
 | ||||
|         return $exception; | ||||
|     } | ||||
| 
 | ||||
|     private function readTemplateInformation(): void | ||||
|     { | ||||
|         $template = null; | ||||
|         $templateClass = null; | ||||
| 
 | ||||
|         $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); | ||||
|         foreach ($backtrace as $trace) { | ||||
|             if (isset($trace['object']) && $trace['object'] instanceof Template) { | ||||
|                 $currentClass = \get_class($trace['object']); | ||||
|                 $isEmbedContainer = 0 === strpos($templateClass, $currentClass); | ||||
|                 if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) { | ||||
|                     $template = $trace['object']; | ||||
|                     $templateClass = \get_class($trace['object']); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // update template name
 | ||||
|         if (null !== $template && null === $this->name) { | ||||
|             $this->name = $template->getTemplateName(); | ||||
|         } | ||||
| 
 | ||||
|         // update template path if any
 | ||||
|         if (null !== $template && null === $this->sourcePath) { | ||||
|             $src = $template->getSourceContext(); | ||||
|             $this->sourceCode = $src->getCode(); | ||||
|             $this->sourcePath = $src->getPath(); | ||||
|         } | ||||
| 
 | ||||
|         if (null === $template || $this->lineno > -1) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         $r = new \ReflectionObject($template); | ||||
|         $file = $r->getFileName(); | ||||
| 
 | ||||
|         $exceptions = [$e = $this]; | ||||
|         while ($e = $e->getPrevious()) { | ||||
|             $exceptions[] = $e; | ||||
|         } | ||||
| 
 | ||||
|         while ($e = array_pop($exceptions)) { | ||||
|             $traces = $e->getTrace(); | ||||
|             array_unshift($traces, ['file' => $e->getFile(), 'line' => $e->getLine()]); | ||||
| 
 | ||||
|             while ($trace = array_shift($traces)) { | ||||
|                 if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) { | ||||
|                     continue; | ||||
|                 } | ||||
| 
 | ||||
|                 foreach ($template->getDebugInfo() as $codeLine => $templateLine) { | ||||
|                     if ($codeLine <= $trace['line']) { | ||||
|                         // update template line
 | ||||
|                         $this->lineno = $templateLine; | ||||
| 
 | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -3,5 +3,5 @@ | ||||
| namespace Picea\Extension; | ||||
| 
 | ||||
| interface Extension { | ||||
|     public function parse(\Picae\Compiler\Context &$context, string $sourceCode, string $token); | ||||
|     public function parse(\Picea\Compiler\Context &$context, string $sourceCode, string $token); | ||||
| } | ||||
|  | ||||
| @ -10,7 +10,7 @@ class JsonExtension implements Extension, FunctionExtension { | ||||
|      | ||||
|     public int $flags =  JSON_HEX_TAG | \JSON_HEX_QUOT | \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_UNICODE; | ||||
|     | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) | ||||
|     { | ||||
|         $flag = $this->flags; | ||||
| 
 | ||||
| @ -20,7 +20,7 @@ class JsonExtension implements Extension, FunctionExtension { | ||||
|                 break; | ||||
| 
 | ||||
|             case "json.html": | ||||
|                 return "<?php echo htmlentities(json_encode($arguments, {$this->flags}), ENT_QUOTES, 'UTF-8') ?>"; | ||||
|                 return "<?php echo htmlentities(json_encode($arguments, $flag), ENT_QUOTES, 'UTF-8') ?>"; | ||||
|         } | ||||
| 
 | ||||
|         $cls = static::class; | ||||
| @ -32,7 +32,7 @@ class JsonExtension implements Extension, FunctionExtension { | ||||
|     { | ||||
|         return [ | ||||
|             'json' => function($arguments, ? int $flags = null) { | ||||
|                 return json_encode($arguments, \JSON_FORCE_OBJECT); | ||||
|                 return json_encode($arguments, $flags ?? $this->flags); | ||||
|             }, | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| namespace Picea\Extension; | ||||
| 
 | ||||
| use Picea\Compiler\Context; | ||||
| use Picea\Event\Builder\ClassTemplateRenderSectionDone; | ||||
| 
 | ||||
| class LanguageExtension implements Extension, FunctionExtension { | ||||
| 
 | ||||
| @ -24,11 +25,26 @@ class LanguageExtension implements Extension, FunctionExtension { | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string | ||||
|     { | ||||
|         switch($token) { | ||||
|             case "language.set": | ||||
|                 return "<?php \$picea->compiler->getExtensionFromToken('$token')->currentLanguage = $arguments; ?>"; | ||||
|                 $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": | ||||
|                 return "<?php echo htmlspecialchars(\$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>"; | ||||
| @ -59,4 +75,21 @@ class LanguageExtension implements Extension, FunctionExtension { | ||||
|     { | ||||
|         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; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,7 +4,7 @@ namespace Picea\Extension; | ||||
| 
 | ||||
| use Picea\Compiler\Context; | ||||
| 
 | ||||
| class MoneyExtension implements Extension { | ||||
| class MoneyExtension implements Extension, FunctionExtension { | ||||
| 
 | ||||
|     public string $token = "money"; | ||||
| 
 | ||||
| @ -14,18 +14,19 @@ class MoneyExtension implements Extension { | ||||
| 
 | ||||
|     public \NumberFormatter $formatter; | ||||
| 
 | ||||
|     public function __construct(Context $context) { | ||||
|         $this->register($context); | ||||
|     public function __construct() { | ||||
|         $this->locale = explode('.', \Locale::getDefault())[0]; | ||||
|         $this->formatter = new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY); | ||||
|     } | ||||
| 
 | ||||
|     public function register(Context $context) : void | ||||
|     public function exportFunctions(): array | ||||
|     { | ||||
|         $context->pushFunction("money", [ $this, 'money' ]); | ||||
|         return [ | ||||
|             "money" => [ $this, 'money' ] | ||||
|         ]; | ||||
|     } | ||||
|      | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
| 
 | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         return "<?php echo money($arguments) ?>"; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ class PhpExtension implements Extension { | ||||
| 
 | ||||
|     public array $token = [ "php" ]; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         return "<?php $arguments ?>"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -6,7 +6,7 @@ use Picea\Compiler\Context; | ||||
| 
 | ||||
| class PrintExtension implements Extension { | ||||
| 
 | ||||
|     public array $token = [ "print", "print.safe", "print.raw" ]; | ||||
|     public array $token = [ "echo", "echo.raw", "print",  "print.raw", "printf", "printf.raw" ]; | ||||
|      | ||||
|     public int $flag = \ENT_QUOTES; | ||||
| 
 | ||||
| @ -18,15 +18,22 @@ class PrintExtension implements Extension { | ||||
|         $this->encoding = ini_get("default_charset"); | ||||
|     } | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) : string | ||||
|     { | ||||
|         switch($token) { | ||||
|             case 'echo': | ||||
|             case 'print': | ||||
|             case "print.safe": | ||||
|                 return "<?php echo htmlspecialchars(sprintf($arguments), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; | ||||
|                 return "<?php echo htmlspecialchars((string) $arguments, {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; | ||||
| 
 | ||||
|             case 'echo.raw': | ||||
|             case "print.raw": | ||||
|                 return "<?php echo sprintf($arguments) ?>"; | ||||
|                 return "<?php echo $arguments ?>"; | ||||
| 
 | ||||
|             case 'printf': | ||||
|                 return "<?php echo htmlspecialchars(sprintf((string) $arguments), {$this->flag}, '{$this->encoding}', " . ($this->doubleEncode ? "true" : "false") . ") ?>"; | ||||
| 
 | ||||
|             case 'printf.raw': | ||||
|                 return "<?php printf((string) $arguments) ?>"; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
| @ -2,25 +2,27 @@ | ||||
| 
 | ||||
| namespace Picea\Extension; | ||||
| 
 | ||||
| use Picea\Compiler\Context; | ||||
| 
 | ||||
| class TitleExtension implements Extension { | ||||
| class TitleExtension implements Extension, FunctionExtension { | ||||
| 
 | ||||
|     public string $token = "title"; | ||||
|      | ||||
|     public string $title = ""; | ||||
|      | ||||
|     public function __construct(Context $context) { | ||||
|         $this->register($context); | ||||
| 
 | ||||
|     public function exportFunctions(): array | ||||
|     { | ||||
|         return [ | ||||
|             "title" => [$this, 'handleTitle'], | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function register(Context $context) : void | ||||
|     { | ||||
|         $context->pushFunction("title", [ $this, 'handleTitle' ]); | ||||
|     } | ||||
|      | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { | ||||
|         return "<?php echo title($arguments) ?>"; | ||||
|     public function parse(\Picea\Compiler\Context &$context, ?string $arguments, string $token) { | ||||
|         return <<<PHP | ||||
|             <?php  | ||||
|                 if ( null !== \$title = title($arguments) ) { | ||||
|                     echo \$title; | ||||
|                 }     | ||||
|             ?>
 | ||||
|         PHP; | ||||
|     } | ||||
|      | ||||
|     public function handleTitle(? string $set = null, ...$arguments) : ? string | ||||
|  | ||||
| @ -4,7 +4,15 @@ namespace Picea\Extension; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| @ -14,15 +22,14 @@ class UrlExtension implements Extension { | ||||
| 
 | ||||
|     protected array $routesTarget; | ||||
| 
 | ||||
|     public array $tokens = [ "url" , "route", "asset", "url.current", "url.parameters", "slug" ]; | ||||
|      | ||||
|     public function __construct(Context $context, string $urlBase = "", string $assetToken = "") { | ||||
|     public array $tokens = [ "url" , "route", "route.cacheless", "asset", "url.current", "url.parameters", "slug" ]; | ||||
| 
 | ||||
|     public function __construct(string $urlBase = "", string $assetToken = "") { | ||||
|         $this->urlBase = trim($urlBase, "/"); | ||||
|         $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 | ||||
|     { | ||||
|         switch($token) { | ||||
|             case "asset": | ||||
| @ -41,19 +48,24 @@ class UrlExtension implements Extension { | ||||
|                 return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>"; | ||||
| 
 | ||||
|             case "slug": | ||||
|                 return \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate( $arguments ); | ||||
|                 return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->slug($arguments) ?>"; | ||||
| 
 | ||||
|                 #return \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate( $arguments );
 | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
|      | ||||
|     public function register(Context $context) : void | ||||
| 
 | ||||
|     public function exportFunctions(): array | ||||
|     { | ||||
|         $context->pushFunction("url",   [ $this, 'buildUrl' ]); | ||||
|         $context->pushFunction("current_url",   [ $this, 'currentUrl' ]); | ||||
|         $context->pushFunction("asset", [ $this, 'buildAssetUrl' ]); | ||||
|         $context->pushFunction("route", [ $this, 'buildRouteUrl' ]); | ||||
|         $context->pushFunction("slug", [ $this, 'slug' ]); | ||||
|         return [ | ||||
|             "url" => [ $this, 'buildUrl' ], | ||||
|             "current_url" => [ $this, 'currentUrl' ], | ||||
|             "asset" => [ $this, 'buildAssetUrl' ], | ||||
|             "route" => [ $this, 'buildRouteUrl' ], | ||||
|             "slug" => [ $this, 'slug' ], | ||||
|             "urlize" => [ $this, 'urlize' ] | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     public function getRouteList(bool $full = false) : array | ||||
| @ -66,25 +78,36 @@ class UrlExtension implements Extension { | ||||
|         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 | ||||
|     { | ||||
|         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 ) ) { | ||||
|             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); | ||||
| @ -100,9 +123,14 @@ class UrlExtension implements Extension { | ||||
|         return $this->scheme() . $this->domain() . $this->base(); | ||||
|     } | ||||
| 
 | ||||
|     # src: https://stackoverflow.com/a/14550919
 | ||||
|     public static function slug(string $text, string $separator = '-') : string | ||||
|     { | ||||
|         return str_replace('-', $separator, \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate(str_replace('-', ' ', $text))); | ||||
|         $clean = iconv('UTF-8', 'ASCII//TRANSLIT', $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 | ||||
| @ -174,12 +202,12 @@ class UrlExtension implements Extension { | ||||
|             $_SERVER['Front-End-Https'] ?? "", | ||||
|             $_SERVER['X-Forwarded-Proto'] ?? "", | ||||
|             $_SERVER['X-Forwarded-Protocol'] ?? "", | ||||
|             $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "", | ||||
| 	        $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "", | ||||
|             $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] ?? "", | ||||
|         ])) || isset($_SERVER['HTTP_X_ARR_SSL']); | ||||
| 
 | ||||
|         return $https | ||||
|             || ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) | ||||
|             || ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) || ( "443" === ( $_SERVER['HTTP_X_FORWARDED_PORT'] ?? "" ) ) | ||||
|             || ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? $_SERVER['HTTP_X_FORWARDED_SSL'] ?? $_SERVER['X-Forwarded-Ssl'] ?? "off")) ); | ||||
|     } | ||||
| 
 | ||||
| @ -193,7 +221,7 @@ class UrlExtension implements Extension { | ||||
|                     list($variable, $default) = explode('=', $item[1]); | ||||
|                 } | ||||
|                 elseif (strpos($item[1], ":") !== false) { | ||||
|                     list($variable, $type) = explode(':', $item[1]); | ||||
|                     list($variable, ) = explode(':', $item[1]); | ||||
|                 } | ||||
|                 else { | ||||
|                     $variable = $item[1]; | ||||
| @ -201,7 +229,6 @@ class UrlExtension implements Extension { | ||||
| 
 | ||||
|                 if ( array_key_exists($variable, $arguments) ) { | ||||
|                     $value = $arguments[ $variable ]; | ||||
| 
 | ||||
|                     unset($arguments[ $variable ]); | ||||
|                 } | ||||
|                 else { | ||||
| @ -209,6 +236,7 @@ class UrlExtension implements Extension { | ||||
|                 } | ||||
| 
 | ||||
|                 $search[ $item[0] ] = $value; | ||||
| 
 | ||||
|             } | ||||
| 
 | ||||
|             $route = str_replace(array_keys($search), array_values($search), $route); | ||||
| @ -226,5 +254,4 @@ class UrlExtension implements Extension { | ||||
| 
 | ||||
|         return $route; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -2,6 +2,8 @@ | ||||
| 
 | ||||
| namespace Picea; | ||||
| 
 | ||||
| use RecursiveIteratorIterator, RecursiveDirectoryIterator; | ||||
| 
 | ||||
| class FileFetcher | ||||
| { | ||||
|     protected array $folderList = []; | ||||
| @ -12,13 +14,13 @@ class FileFetcher | ||||
| 
 | ||||
|     public function __construct(?array $folderList = null) { | ||||
|         if ( $folderList !== null ) { | ||||
|             $this->folderList = $folderList; | ||||
|             $this->addFolders($folderList); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public function addFolder(string $folder, int $order = 100) : void | ||||
|     { | ||||
|         $folder = rtrim($folder, DIRECTORY_SEPARATOR); | ||||
|         $folder = $this->normalizeFolder($folder); | ||||
| 
 | ||||
|         $this->folderList[$folder] = [ | ||||
|             'path' => $folder, | ||||
| @ -28,7 +30,14 @@ class FileFetcher | ||||
| 
 | ||||
|     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 | ||||
| @ -40,7 +49,7 @@ class FileFetcher | ||||
|     { | ||||
|         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) { | ||||
|                 $file = $folder['path'] . DIRECTORY_SEPARATOR . "$fileName.$extension"; | ||||
|                 $file = str_replace([ '\\', '/' ], DIRECTORY_SEPARATOR, $file); | ||||
| @ -56,7 +65,7 @@ class FileFetcher | ||||
|         } | ||||
| 
 | ||||
|         # Fallback on full-path
 | ||||
|         foreach($this->folderList as $folder) { | ||||
|         foreach($this->folderList() as $folder) { | ||||
|             $file = $folder['path'] . DIRECTORY_SEPARATOR . $fileName; | ||||
|             $file = str_replace([ '\\', '/' ], DIRECTORY_SEPARATOR, $file); | ||||
| 
 | ||||
| @ -72,6 +81,30 @@ class FileFetcher | ||||
|         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 | ||||
|     { | ||||
|         return $this->findFile($fileName); | ||||
| @ -81,4 +114,5 @@ class FileFetcher | ||||
|     { | ||||
|         return file_get_contents($this->getFilePath($fileName)); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -28,7 +28,6 @@ class DefaultRegistrations implements LanguageRegistration | ||||
| 
 | ||||
|     public function registerSyntax(Compiler $compiler) : void | ||||
|     { | ||||
|         $compiler->registerSyntax(new \Picea\Syntax\PhpTagToken()); | ||||
|         $compiler->registerSyntax(new \Picea\Syntax\CommentToken()); | ||||
|         $compiler->registerSyntax(new \Picea\Syntax\EchoRawToken()); | ||||
|         $compiler->registerSyntax(new \Picea\Syntax\EchoSafeToken()); | ||||
| @ -45,6 +44,7 @@ class DefaultRegistrations implements LanguageRegistration | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\IfToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\ForeachToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\ForToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\WhileToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\OrToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\SwitchToken()); | ||||
|         $compiler->registerControlStructure(new \Picea\ControlStructure\DefaultToken()); | ||||
|  | ||||
| @ -7,10 +7,11 @@ use Picea\Extension\Extension, | ||||
| 
 | ||||
| use Picea\Compiler\Context; | ||||
| 
 | ||||
| use Picea\Extension\FunctionExtension; | ||||
| use Psr\Http\Message\RequestInterface; | ||||
| use Psr\Http\Message\ServerRequestInterface; | ||||
| 
 | ||||
| class Request implements Extension { | ||||
| class Request implements Extension, FunctionExtension { | ||||
|     use ExtensionTrait; | ||||
| 
 | ||||
|     public array $tokens; | ||||
| @ -21,20 +22,21 @@ class Request implements Extension { | ||||
| 
 | ||||
|     public function __construct(ServerRequestInterface $request, Context $context) { | ||||
|         $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' ]); | ||||
|         $context->pushFunction("get",  [ $this, 'get'  ]); | ||||
|         $context->pushFunction("post", [ $this, 'post' ]); | ||||
|         $context->pushFunction("request", [ $this, 'request' ]); | ||||
|         $context->pushFunction("server", [ $this, 'server' ]); | ||||
|         return [ | ||||
|             "cookie" => [ $this, 'cookie' ], | ||||
|             "get" => [ $this, 'get' ], | ||||
|             "post" => [ $this, 'post' ], | ||||
|             "request" => [ $this, 'request' ], | ||||
|             "server" => [ $this, 'server' ], | ||||
|         ]; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     public function cookie(? string $variableName = null, $default = null) | ||||
|     { | ||||
|         return $variableName === null ? $this->request->getCookieParams() : static::arrayGet($this->request->getCookieParams(), $variableName) ?? $default; | ||||
|  | ||||
| @ -20,8 +20,6 @@ class Picea implements LanguageRegistration | ||||
| 
 | ||||
|     public string $builderTemplatePath; | ||||
| 
 | ||||
|     public Closure $responseHtml; | ||||
| 
 | ||||
|     public Caching\Cache $cache; | ||||
| 
 | ||||
|     public FileFetcher $fileFetcher; | ||||
| @ -35,7 +33,6 @@ class Picea implements LanguageRegistration | ||||
|     public array $compiled = []; | ||||
| 
 | ||||
|     public function __construct( | ||||
|         ? Closure $responseHtml = null, | ||||
|         ? Compiler\Context $context = null, | ||||
|         ? Caching\Cache $cache = null, | ||||
|         ? Compiler $compiler = null, | ||||
| @ -44,7 +41,6 @@ class Picea implements LanguageRegistration | ||||
|         ? string $builderTemplatePath = null, | ||||
|         bool $debug = false | ||||
|     ){ | ||||
|         $this->response = $responseHtml; | ||||
|         $this->cache = $cache ?? new Caching\Memory(""); | ||||
|         $this->context = $context ?? new Compiler\BaseContext(); | ||||
|         $this->languageRegistration = $languageRegistration ?? new Language\DefaultRegistrations(); | ||||
| @ -58,7 +54,7 @@ class Picea implements LanguageRegistration | ||||
|         $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) ) { | ||||
|             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 | ||||
|     { | ||||
|         $object = $this->gatherCompiledObject($viewPath, $variables, $proxy); | ||||
| 
 | ||||
|         try { | ||||
|             return call_user_func($this->gatherTemplateObject($viewPath, $variables, $proxy)); | ||||
|             return call_user_func($object); | ||||
|         } | ||||
|         catch(\Throwable $ex) { | ||||
|             # Temporary class for an experiment
 | ||||
|             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 | ||||
|                 { | ||||
|                     $content = null; | ||||
| 
 | ||||
|                     if ( is_array($content) && isset($content['classname'], $content['namespace'], $content['view'], $content['extends']) ) { | ||||
|                         return $content; | ||||
|                     } | ||||
| 
 | ||||
|                     return null; | ||||
|                 } | ||||
| 
 | ||||
|             }; | ||||
|             if (! $ex instanceof Exception\RenderHtmlException ) { | ||||
|                 throw new Exception\RenderHtmlException($object, $this, "An error occurred trying to render HTML view `$viewPath` : " . $ex->getMessage(), 911, $ex); | ||||
|             } | ||||
|             else { | ||||
|                 throw $ex; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         exit(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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) { | ||||
|         return $this->renderHtml($viewPath, $this->globalVariables + $variables, $proxy); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Allows block to be called from templates | ||||
|      */ | ||||
|     public function inlineBlock(? object $proxy, string $viewPath, ... $variables) { | ||||
|         return $this->renderHtml($viewPath, [ 'inlineVariables' => $variables, 'globalVariables' => $this->globalVariables ], $proxy); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /** | ||||
|      * Push view content inline | ||||
|      */ | ||||
|     public function inlineContent(string $viewPath) { | ||||
|         return $this->fileFetcher->getFileContent($viewPath); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /** | ||||
|      * Renders a compile context | ||||
|      */ | ||||
|     public function renderContext(Compiler\Context $context) : object | ||||
|     { | ||||
|         if ( null === $object = $this->contextFromCache($context) ) { | ||||
|  | ||||
| @ -10,7 +10,7 @@ class CommentToken implements Syntax { | ||||
| 
 | ||||
|     protected string $tokenClose = "\#\}"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, string &$sourceCode) | ||||
|     public function parse(\Picea\Compiler\Context &$context, string &$sourceCode) | ||||
|     { | ||||
|         $sourceCode = preg_replace("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", "", $sourceCode); | ||||
|     } | ||||
|  | ||||
| @ -8,7 +8,7 @@ class EchoRawToken implements Syntax { | ||||
| 
 | ||||
|     public string $tokenClose = "\}\}"; | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$content, string &$sourceCode) | ||||
|     public function parse(\Picea\Compiler\Context &$content, string &$sourceCode) | ||||
|     { | ||||
|         $sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) { | ||||
|             $line = trim($matches[2], " \t\n\r\0\x0B;"); | ||||
|  | ||||
| @ -18,12 +18,12 @@ class EchoSafeToken implements Syntax { | ||||
|         $this->encoding = ini_get("default_charset"); | ||||
|     } | ||||
| 
 | ||||
|     public function parse(/*\Picae\Compiler\Context*/ &$context, string &$sourceCode) | ||||
|     public function parse(\Picea\Compiler\Context &$context, string &$sourceCode) | ||||
|     { | ||||
|         $sourceCode = preg_replace_callback("#({$this->tokenOpen})(.*?)({$this->tokenClose})#s", function ($matches) { | ||||
|             $line = trim($matches[2], " \t\n\r\0\x0B;"); | ||||
|              | ||||
|             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); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -3,5 +3,5 @@ | ||||
| namespace Picea\Syntax; | ||||
| 
 | ||||
| interface Syntax { | ||||
|     public function parse(\Picae\Compiler\Context &$context, string &$sourceCode); | ||||
|     public function parse(\Picea\Compiler\Context &$context, string &$sourceCode); | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user