diff --git a/src/Builder/ClassTemplate.php b/src/Builder/ClassTemplate.php index 47b28bd..5af29a0 100644 --- a/src/Builder/ClassTemplate.php +++ b/src/Builder/ClassTemplate.php @@ -20,6 +20,10 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { public static $context; + public bool $rendering = false; + + public bool $renderingInsideSection = false; + public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) { $this->picea = $picea; $this->variableList = $variablesList; @@ -33,32 +37,49 @@ if (! class_exists("%NAMESPACE%\%CLASSNAME%", false) ) { public function output(array $variablesList = []) : void { + $this->rendering = true; + ( function($___class__template, $___global_variables, $___variables, $picea) { extract($___global_variables); extract($___variables, \EXTR_OVERWRITE); ?>%CONTENT%call($this->thisProxy ?? new class(){}, $this, $this->variableList, $variablesList, $this->picea); %PARENT_OUTPUT% + + $this->rendering = false; } - public function renderSection($name) : void + public function renderSection(string $name, bool $return = false) /*: string|void*/ { + $result = []; + foreach([ 'prepend', 'default', 'append' ] as $item) { + if ( ! is_array($this->sectionList[$name][$item]) ) continue; + usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']); foreach($this->sectionList[$name][$item] as $section) { + if ($return) { + ob_start(); + } + $section['callback'](); + if ($return) { + $result[] = ob_get_clean(); + } + if ( $item === 'default' ) { break 1; } } } + + return $return ? implode("", $result) : ""; } public function exportFunctions() : void { - static $caching = []; %FUNCTIONS% } diff --git a/src/Compiler.php b/src/Compiler.php index ab3d8ba..3779495 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -2,6 +2,7 @@ namespace Picea; +use Picea\Extension\FunctionExtension; use Picea\Language\LanguageRegistration; class Compiler @@ -94,13 +95,35 @@ class Compiler public function registerExtension(Extension\Extension $extension) : self { - foreach($extension->tokens ?? (array) ( $extension->token ?? [] ) as $token) { + $tokens = $extension->tokens ?? (array) ( $extension->token ?? [] ); + + foreach($tokens as $token) { $this->extensionList[$token] = $extension; } + if (! $tokens ) { + $this->extensionList[] = $extension; + } + return $this; } - + + public function registerExtensionsFunctions(Compiler\Context $context) : void + { + foreach($this->extensionList as $ext) { + if ($ext instanceof FunctionExtension) { + foreach ($ext->exportFunctions() as $name => $value) { + if ( is_string($value) ) { + $callable = fn(...$args) => call_user_func_array([ $ext, $value ], $args); + } + + $context->pushFunction(is_numeric($name) ? $value : $name, $callable ?? $value); + } + } + } + + } + public function getExtensionFromToken(string $name) : Extension\Extension { if ( false === $this->extensionList[$name] ?? false ) { diff --git a/src/Compiler/Context.php b/src/Compiler/Context.php index 62730b6..0cbf307 100644 --- a/src/Compiler/Context.php +++ b/src/Compiler/Context.php @@ -54,11 +54,17 @@ abstract class Context { public function renderFunctions() : string { - $cls = $this->compiledClassPath(); - $ns = $this->namespace ? "\\{$this->namespace}\\" : ""; + if ($this->extendFrom) { + return "parent::exportFunctions();"; + } + else { + $cls = $this->compiledClassPath(); + $ns = $this->namespace ? "\\{$this->namespace}\\" : ""; - foreach($this->functionStack as $name => $function) { - $list[] = <<functionStack as $name => $function) { + $list[] = << PHP; diff --git a/src/ControlStructure/ForToken.php b/src/ControlStructure/ForToken.php index 0cb66c4..9f44026 100644 --- a/src/ControlStructure/ForToken.php +++ b/src/ControlStructure/ForToken.php @@ -20,6 +20,10 @@ class ForToken implements ControlStructure { return ""; case "endfor": + if ( $context->iterationStack === null ){ + + } + if ( end($context->iterationStack)['or'] === false ) { $output = ""; } @@ -30,6 +34,9 @@ class ForToken implements ControlStructure { array_pop($context->iterationStack); return $output; + + case "continue": + return ""; } } } diff --git a/src/ControlStructure/ForeachToken.php b/src/ControlStructure/ForeachToken.php index 7326a5d..e5c8d91 100644 --- a/src/ControlStructure/ForeachToken.php +++ b/src/ControlStructure/ForeachToken.php @@ -4,10 +4,13 @@ namespace Picea\ControlStructure; class ForeachToken implements ControlStructure { - public array $token = [ "foreach", "endforeach" ]; + public array $token = [ "foreach", "endforeach", "continue" ]; public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { switch($token) { + case "continue": + return ""; + case "foreach": $name = "$".uniqid("foreach_"); @@ -27,7 +30,7 @@ class ForeachToken implements ControlStructure { 'token' => 'endforeach', ]; - return ""; + return ""; case "endforeach": if ( end($context->iterationStack)['or'] === false ) { diff --git a/src/ControlStructure/SectionToken.php b/src/ControlStructure/SectionToken.php index 196d539..08543ad 100644 --- a/src/ControlStructure/SectionToken.php +++ b/src/ControlStructure/SectionToken.php @@ -47,13 +47,13 @@ class SectionToken implements ControlStructure { return "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); ?>"; + "extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); \$___class__template->sectionStack[] = '$name'; ?>"; } protected function printEndSection($context) : string { $section = array_pop($context->sections); - $build = $context->extendFrom ? "" : "\$___class__template->renderSection({$section['name']});"; - return ""; + $build = $context->extendFrom ? "\$___class__template->sectionStack && \$___class__template->renderSection({$section['name']});" : "\$___class__template->renderSection({$section['name']});"; + return "sectionStack); }]; $build?>"; } } \ No newline at end of file diff --git a/src/Extension/FunctionExtension.php b/src/Extension/FunctionExtension.php new file mode 100644 index 0000000..02e238d --- /dev/null +++ b/src/Extension/FunctionExtension.php @@ -0,0 +1,10 @@ +register($context); - } - } - - public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) + + public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { $flag = $this->flags; - + switch ($token) { case "json.pretty": $flag |= \JSON_PRETTY_PRINT; - break; - + break; + case "json.html": return "flags}), ENT_QUOTES, 'UTF-8') ?>"; } return ""; } - - public function register(Context $context) : void + + public function exportFunctions(): array { - $context->pushFunction("json", function($arguments, ? int $flags = null) { - return json_encode($arguments, \JSON_FORCE_OBJECT); - }); + return [ + 'json' => function($arguments, ? int $flags = null) { + return json_encode($arguments, \JSON_FORCE_OBJECT); + }, + ]; } + } diff --git a/src/Extension/LanguageExtension.php b/src/Extension/LanguageExtension.php index 75e9397..8949b9a 100644 --- a/src/Extension/LanguageExtension.php +++ b/src/Extension/LanguageExtension.php @@ -4,7 +4,7 @@ namespace Picea\Extension; use Picea\Compiler\Context; -class LanguageExtension implements Extension { +class LanguageExtension implements Extension, FunctionExtension { public array $tokens = [ "lang", "_", "language.set" ]; @@ -12,11 +12,18 @@ class LanguageExtension implements Extension { protected LanguageHandler $languageHandler; - public function __construct(Context $context, LanguageHandler $handler) { - $this->register($context); + public function __construct(LanguageHandler $handler) { $this->languageHandler = $handler; } + public function exportFunctions(): array + { + return [ + '_' => 'relativeLang', + 'lang' => 'absoluteLang', + ]; + } + public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string { switch($token) { @@ -35,8 +42,6 @@ class LanguageExtension implements Extension { public function register(Context $context) : void { - $context->pushFunction("_", [ $this, 'relativeLang' ]); - $context->pushFunction("lang", [ $this, 'absoluteLang' ]); } public function relativeLang(string $key, array $variables = []) #: array|string diff --git a/src/Extension/UrlExtension.php b/src/Extension/UrlExtension.php index 0fcbd33..ca50132 100644 --- a/src/Extension/UrlExtension.php +++ b/src/Extension/UrlExtension.php @@ -14,7 +14,7 @@ class UrlExtension implements Extension { protected array $routesTarget; - public array $tokens = [ "url" , "route", "asset", "url.parameters" ]; + public array $tokens = [ "url" , "route", "asset", "url.current", "url.parameters" ]; public function __construct(Context $context, string $urlBase = "", string $assetToken = "") { $this->urlBase = trim($urlBase, "/"); @@ -33,7 +33,10 @@ class UrlExtension implements Extension { case "url": return "compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>"; - + + case "url.current": + return "compiler->getExtensionFromToken('$token')->currentUrl($arguments) ?>"; + case "url.parameters": return "compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>"; } @@ -44,6 +47,7 @@ class UrlExtension implements Extension { public function register(Context $context) : void { $context->pushFunction("url", [ $this, 'buildUrl' ]); + $context->pushFunction("current_url", [ $this, 'currentUrl' ]); $context->pushFunction("asset", [ $this, 'buildAssetUrl' ]); $context->pushFunction("route", [ $this, 'buildRouteUrl' ]); } @@ -58,6 +62,11 @@ class UrlExtension implements Extension { return $url . ( $parameters ? "?" . http_build_query($parameters) : "" ); } + public function currentUrl(array $parameters = []) : string + { + return $this->buildUrl($this->uri(), $parameters); + } + public function buildUrl(string $uri = "", array $parameters = []) : string { return $this->setUrlParameters($this->url() . "/" . ltrim($uri, "/"), $parameters); @@ -108,11 +117,7 @@ class UrlExtension implements Extension { $uri = explode('?', $uri, 2)[0]; } - if ( ($base = $this->config['base'] ?? false) && ( stripos($uri, $base) === 0 ) ) { - $uri = substr($uri, strlen($base)); - } - - return '/' . ltrim($uri, '/'); + return '/' . ltrim(substr($uri, strlen($this->base())), '/'); } /** @@ -177,23 +182,23 @@ class UrlExtension implements Extension { if (strpos($item[1], "=") !== false) { list($variable, $default) = explode('=', $item[1]); } + elseif (strpos($item[1], ":") !== false) { + list($variable, ) = explode(':', $item[1]); + } else { $variable = $item[1]; } - + if ( array_key_exists($variable, $arguments) ) { - $value = $arguments[ $item[1] ]; - } - else if ( isset($default) ) { - $value = ""; # $default; + $value = $arguments[ $variable ]; + unset($arguments[ $variable ]); } else { - $variable = $item[1]; + $value = $default ?? ""; } $search[ $item[0] ] = $value; - unset($arguments[ $item[1] ]); } $route = str_replace(array_keys($search), array_values($search), $route); diff --git a/src/Language/DefaultRegistrations.php b/src/Language/DefaultRegistrations.php index 6ecebd6..6147c60 100644 --- a/src/Language/DefaultRegistrations.php +++ b/src/Language/DefaultRegistrations.php @@ -4,8 +4,6 @@ namespace Picea\Language; use Picea\Compiler; -use Picea\Compiler\Context; - class DefaultRegistrations implements LanguageRegistration { protected array $extensions; @@ -13,12 +11,9 @@ class DefaultRegistrations implements LanguageRegistration protected array $syntaxes; protected array $controlStructures; - - protected ? Context $context; - - public function __construct(? Context $context = null, array $extensions = [], array $syntaxes = [], array $controlStructure = []) + + public function __construct(array $extensions = [], array $syntaxes = [], array $controlStructure = []) { - $this->context = $context; $this->extensions = $extensions; $this->syntaxes = $syntaxes; $this->controlStructures = $controlStructure; @@ -69,8 +64,8 @@ class DefaultRegistrations implements LanguageRegistration { $compiler->registerExtension(new \Picea\Extension\PhpExtension()); $compiler->registerExtension(new \Picea\Extension\PrintExtension()); - $compiler->registerExtension(new \Picea\Extension\JsonExtension($this->context)); - + $compiler->registerExtension(new \Picea\Extension\JsonExtension()); + foreach($this->extensions ?? [] as $extension) { $compiler->registerExtension($extension); } diff --git a/src/Picea.php b/src/Picea.php index 0a693e8..33db417 100644 --- a/src/Picea.php +++ b/src/Picea.php @@ -30,6 +30,10 @@ class Picea implements LanguageRegistration public array $loadedTemplateFile = []; + public array $globalVariables = []; + + public array $compiled = []; + public function __construct( ? Closure $responseHtml = null, ? Compiler\Context $context = null, @@ -48,19 +52,25 @@ class Picea implements LanguageRegistration $this->compiler = $compiler ?? $this->instanciateCompiler(); $this->fileFetcher = $fileFetcher ?? $this->instanciateFileFetcher(); $this->debug = $debug; - - #$this->context->exportFunctions(); + + $this->compiler->registerExtensionsFunctions($this->context); + $this->renderContext($this->context); } + public function gatherTemplateObject(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."); + } + + return $object; + } + public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : ? string { - if ( null === $object = $this->fetchFromCache($viewPath, $variables, $proxy) ) { - throw new \RuntimeException("An error occured while trying to save a compiled template."); - } - try { - return $object(); + return call_user_func($this->gatherTemplateObject($viewPath, $variables, $proxy)); } catch(\Throwable $ex) { # Temporary class for an experiment @@ -120,11 +130,11 @@ class Picea implements LanguageRegistration * @return type */ public function inlineHtml(? object $proxy, string $viewPath, array $variables) { - return $this->renderHtml($viewPath, $variables, $proxy); + return $this->renderHtml($viewPath, $this->globalVariables + $variables, $proxy); } public function inlineBlock(? object $proxy, string $viewPath, ... $variables) { - return $this->renderHtml($viewPath, [ 'inlineVariables' => $variables ], $proxy); + return $this->renderHtml($viewPath, [ 'inlineVariables' => $this->globalVariables + $variables ], $proxy); } public function inlineContent(string $viewPath) { @@ -187,13 +197,15 @@ class Picea implements LanguageRegistration public function fetchFromCache(string $viewPath, array $variables = [], ?object $proxy = null) : ?object { - if ( ( true === $this->debug ) || (! $this->cache->compiled($viewPath))) { + if ( ! isset($this->compiled[$viewPath]) && ( true === $this->debug ) || (! $this->cache->compiled($viewPath))) { $context = $this->compileView($viewPath); $this->cache->save($context); if ( $context->extendFrom ) { $this->fetchFromCache($context->extendFrom, $variables, $proxy); } + + $this->compiled[$viewPath] = true; } return $this->cache->load($viewPath, $this, $variables, $proxy);