- Work on Language Handler and extension.
- Added an URL extension which allows routes and URLs. - Fixed some bug within ClassTemplate
This commit is contained in:
parent
a64989af9f
commit
ddc3ae704a
|
@ -19,11 +19,12 @@ class Builder
|
|||
|
||||
$replace = [
|
||||
'%NAMESPACE%' => $context->namespace,
|
||||
'%USE%' => $context->renderUses(),
|
||||
'%USE%' => ( $uses = $context->renderUses() ) ? "use $uses;" : false,
|
||||
'%CLASSNAME%' => $context->className,
|
||||
'%EXTENDS%' => $context->extendFrom ? "extends " . static::TEMPLATE_CLASSNAME_PREFIX . static::generateClassUID($context->extendFrom) : '',
|
||||
'%EXTENDS_TEMPLATE%' => $context->extendFrom,
|
||||
'%CONTENT%' => $compiledSource,
|
||||
'%FUNCTIONS%' => $context->functionStack ? $context->renderFunctions() : "",
|
||||
'%PARENT_OUTPUT%' => $context->extendFrom ? "parent::output(\$variablesList);" : "",
|
||||
];
|
||||
|
||||
|
|
|
@ -8,29 +8,34 @@ class %CLASSNAME% %EXTENDS% {
|
|||
|
||||
public array $sectionList = [];
|
||||
|
||||
public array $variableList = [];
|
||||
public array $variableList = [];
|
||||
|
||||
public ?object $thisProxy = null;
|
||||
|
||||
public \Picea\Picea $picea;
|
||||
|
||||
public static $context;
|
||||
|
||||
public function __construct(\Picea\Picea $picea, array $variablesList = [], ?object $thisProxy = null) {
|
||||
$this->picea = $picea;
|
||||
$this->variableList = $variablesList;
|
||||
$this->thisProxy = $thisProxy;
|
||||
$this->exportFunctions();
|
||||
|
||||
static::$context = $picea->context;
|
||||
}
|
||||
|
||||
public function output(array $variablesList = []) : void
|
||||
{
|
||||
( function($___class__template, $___global_variables, $___variables, $___picea) {
|
||||
extract($___global_variables); unset($___global_variables);
|
||||
extract($___variables, \EXTR_OVERWRITE); unset($___variables);
|
||||
public function output(array $variablesList = []) : void
|
||||
{
|
||||
( function($___class__template, $___global_variables, $___variables, $picea) {
|
||||
extract($___global_variables);
|
||||
extract($___variables, \EXTR_OVERWRITE);
|
||||
?>%CONTENT%<?php
|
||||
} )->call($this->thisProxy ?? new class(){}, $this, $this->variableList, $variablesList, $this->picea);
|
||||
%PARENT_OUTPUT%
|
||||
}
|
||||
}
|
||||
|
||||
public function renderSection($name) : void
|
||||
public function renderSection($name) : void
|
||||
{
|
||||
foreach([ 'prepend', 'default', 'append' ] as $item) {
|
||||
usort($this->sectionList[$name][$item], fn($a, $b) => $a['order'] <=> $b['order']);
|
||||
|
@ -44,8 +49,15 @@ class %CLASSNAME% %EXTENDS% {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function exportFunctions() : void
|
||||
{
|
||||
static $caching = [];
|
||||
%FUNCTIONS%
|
||||
}
|
||||
|
||||
public function __invoke(array $variablesList = []) {
|
||||
public function __invoke(array $variablesList = []) : string
|
||||
{
|
||||
ob_start();
|
||||
$this->output($variablesList);
|
||||
return ob_get_clean();
|
||||
|
|
|
@ -23,11 +23,11 @@ class Compiler
|
|||
public function __construct(?LanguageRegistration $languageRegistration = null)
|
||||
{
|
||||
$this->languageRegistration = $languageRegistration;
|
||||
$this->languageRegistration->registerAll($this);
|
||||
}
|
||||
|
||||
public function compile(Compiler\Context $context) : string
|
||||
{
|
||||
$this->languageRegistration->registerAll($this);
|
||||
$context->compiler = $this;
|
||||
|
||||
/**
|
||||
|
@ -82,7 +82,7 @@ class Compiler
|
|||
|
||||
public function registerControlStructure(ControlStructure\ControlStructure $controlStructureObject) : self
|
||||
{
|
||||
foreach($controlStructureObject->tokens ?? (array) $controlStructureObject->token as $token) {
|
||||
foreach($controlStructureObject->tokens ?? (array) ( $controlStructureObject->token ?? [] ) as $token) {
|
||||
$this->tagList[$token] = $controlStructureObject;
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ class Compiler
|
|||
|
||||
public function registerExtension(Extension\Extension $extension) : self
|
||||
{
|
||||
foreach($extension->tokens ?? (array) $extension->token as $token) {
|
||||
foreach($extension->tokens ?? (array) ( $extension->token ?? [] ) as $token) {
|
||||
$this->extensionList[$token] = $extension;
|
||||
}
|
||||
|
||||
|
@ -108,4 +108,11 @@ class Compiler
|
|||
|
||||
return $this->extensionList[$name];
|
||||
}
|
||||
|
||||
public function exportFunctions() : void
|
||||
{
|
||||
static $caching = [];
|
||||
eval( $this->context->renderFunctions() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ abstract class Context {
|
|||
|
||||
public array $useStack = [];
|
||||
|
||||
public array $functionStack = [];
|
||||
|
||||
public array $hooks = [];
|
||||
|
||||
public Compiler $compiler;
|
||||
|
@ -45,13 +47,38 @@ abstract class Context {
|
|||
|
||||
public function renderUses() : string
|
||||
{
|
||||
return implode(",", $context->useStack ?? []);
|
||||
return implode(",", $this->useStack ?? []);
|
||||
}
|
||||
|
||||
#public function variables(array $variables) : void
|
||||
#{
|
||||
# $this->variables = $variables;
|
||||
#}
|
||||
public function renderFunctions() : string
|
||||
{
|
||||
$cls = $this->compiledClassPath();
|
||||
$ns = $this->namespace ? "\\{$this->namespace}\\" : "";
|
||||
|
||||
foreach($this->functionStack as $name => $function) {
|
||||
$list[] = <<<FUNC
|
||||
if ( false === ( ( \$caching['$ns$name'] ?? false) || function_exists( '$ns$name' ) ) ) {
|
||||
\$caching['$ns$name'] = true;
|
||||
|
||||
function $name(...\$arguments) {
|
||||
return $cls::\$context->functionStack['$name'](...\$arguments);
|
||||
}
|
||||
}
|
||||
FUNC;
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $list);
|
||||
}
|
||||
|
||||
public function exportFunctions() : void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function pushFunction($name, Callable $callable) : void
|
||||
{
|
||||
$this->functionStack[$name] = $callable;
|
||||
}
|
||||
|
||||
public function compiledClassPath() : string
|
||||
{
|
||||
|
@ -61,4 +88,9 @@ abstract class Context {
|
|||
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
public function cacheFilename() : string
|
||||
{
|
||||
return strtolower(str_replace("\\", DIRECTORY_SEPARATOR, $this->namespace)) . ".context";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ 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 (\$___class__template, \$___global_variables, \$___variables) {
|
||||
\$___class__template->sectionList[$name]['$action'][] = [ 'order' => $order, 'callback' => function() use (\$picea, \$___class__template, \$___global_variables, \$___variables) {
|
||||
extract(\$___global_variables); extract(\$___variables, \EXTR_OVERWRITE); ?>";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ class ViewToken implements ControlStructure {
|
|||
public string $token = "view";
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) {
|
||||
return "<?php echo \$___class__template->picea->renderHtml($arguments, get_defined_vars()); ?>";
|
||||
return "<?php echo \$___class__template->picea->renderHtml($arguments, get_defined_vars(), \$this); ?>";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,50 @@
|
|||
|
||||
namespace Picea\Extension;
|
||||
|
||||
use Picea\Compiler\Context;
|
||||
|
||||
class LanguageExtension implements Extension {
|
||||
|
||||
public array $tokens = [ "lang", "_" ];
|
||||
public array $tokens = [ "lang", "_", "language.set" ];
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) {
|
||||
return "<?php /* echo lang($arguments) */ ?>";
|
||||
public string $currentLanguage = "";
|
||||
|
||||
protected LanguageHandler $languageHandler;
|
||||
|
||||
public function __construct(Context $context, LanguageHandler $handler) {
|
||||
$this->register($context);
|
||||
$this->languageHandler = $handler;
|
||||
}
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : string
|
||||
{
|
||||
switch($token) {
|
||||
case "language.set":
|
||||
return "<?php \$picea->compiler->getExtensionFromToken('$token')->currentLanguage = $arguments; ?>";
|
||||
|
||||
case "lang":
|
||||
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->absoluteLang($arguments) ?>";
|
||||
|
||||
case "_":
|
||||
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public function register(Context $context) : void
|
||||
{
|
||||
$context->pushFunction("_", [ $this, 'relativeLang' ]);
|
||||
$context->pushFunction("lang", [ $this, 'absoluteLang' ]);
|
||||
}
|
||||
|
||||
public function relativeLang(string $key, array $variables = []) : string
|
||||
{
|
||||
return $this->languageHandler->languageFromKey("{$this->currentLanguage}.{$key}", $variables);
|
||||
}
|
||||
|
||||
public function absoluteLang(string $key, array $variables = []) : string
|
||||
{
|
||||
return $this->languageHandler->languageFromKey($key, $variables);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Picea\Extension;
|
||||
|
||||
interface LanguageHandler
|
||||
{
|
||||
public function languageFromKey(string $key, array $variables = []) : string;
|
||||
}
|
|
@ -2,50 +2,81 @@
|
|||
|
||||
namespace Picea\Extension;
|
||||
|
||||
use Picea\Compiler\Context;
|
||||
|
||||
class UrlExtension implements Extension {
|
||||
|
||||
protected string $urlBase;
|
||||
|
||||
|
||||
protected string $assetToken;
|
||||
|
||||
|
||||
protected array $routes;
|
||||
|
||||
public array $tokens = [ "url" , "route", "asset" ];
|
||||
|
||||
public function __construct(string $urlBase = "", string $assetToken = "") {
|
||||
public function __construct(Context $context, string $urlBase = "", string $assetToken = "") {
|
||||
$this->urlBase = $urlBase;
|
||||
$this->assetToken = $assetToken;
|
||||
}
|
||||
$this->register($context);
|
||||
}
|
||||
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : ?string
|
||||
public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) : ?string
|
||||
{
|
||||
switch($token) {
|
||||
case "asset":
|
||||
return "<?php echo \$___picea->compiler->getExtensionFromToken('$token')->buildAssetUrl($arguments) ?>";
|
||||
|
||||
case "route":
|
||||
return "<?php echo \$___picea->compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>";
|
||||
|
||||
case "url":
|
||||
return "<?php echo \$___picea->compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>";
|
||||
case "asset":
|
||||
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildAssetUrl($arguments) ?>";
|
||||
|
||||
case "route":
|
||||
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildRouteUrl($arguments) ?>";
|
||||
|
||||
case "url":
|
||||
return "<?php echo \$picea->compiler->getExtensionFromToken('$token')->buildUrl($arguments) ?>";
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function buildUrl(string $uri, array $parameters = []) : string
|
||||
public function register(Context $context) : void
|
||||
{
|
||||
return $this->url() . $uri . ( $parameters ? "?" . http_build_query($parameters) : "" );
|
||||
$context->pushFunction("url", [ $this, 'buildUrl' ]);
|
||||
$context->pushFunction("asset", [ $this, 'buildAssetUrl' ]);
|
||||
$context->pushFunction("route", [ $this, 'buildRouteUrl' ]);
|
||||
}
|
||||
|
||||
|
||||
public function buildUrl(string $uri = "", array $parameters = []) : string
|
||||
{
|
||||
return $this->url() . "/" . ltrim($uri, "/") . ( $parameters ? "?" . http_build_query($parameters) : "" );
|
||||
}
|
||||
|
||||
public function buildAssetUrl(string $uri, array $parameters = []) : string
|
||||
{
|
||||
return $this->url() . $uri . "?" . http_build_query( array_replace([ 'token' => $this->assetToken ], $parameters) );
|
||||
return $this->buildUrl($uri, array_replace([ 'v' => $this->assetToken ], $parameters));
|
||||
}
|
||||
|
||||
|
||||
public function buildRouteUrl(string $name, array $parameters = []) : string
|
||||
{
|
||||
if ( false !== ( $route = $this->routes[$name] ?? false ) ) {
|
||||
return $this->buildUrl($this->prepareRoute($route, $parameters), $parameters);
|
||||
}
|
||||
|
||||
$routeList = json_encode($this->routes, \JSON_PRETTY_PRINT);
|
||||
|
||||
throw new \RuntimeException("
|
||||
Given route `$name` could not be found within current route list:
|
||||
$routeList
|
||||
");
|
||||
}
|
||||
|
||||
public function url() : string
|
||||
{
|
||||
return $this->scheme() . $this->domain() . $this->base();
|
||||
}
|
||||
|
||||
|
||||
public function registerRoute(string $name, string $route) : void
|
||||
{
|
||||
$this->routes[$name] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URI formatted
|
||||
*/
|
||||
|
@ -60,33 +91,33 @@ class UrlExtension implements Extension {
|
|||
if ( ($base = $this->config['base'] ?? false) && ( stripos($uri, $base) === 0 ) ) {
|
||||
$uri = substr($uri, strlen($base));
|
||||
}
|
||||
|
||||
|
||||
return '/' . ltrim($uri, '/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return query string
|
||||
*/
|
||||
protected function queryString() : string
|
||||
protected function queryString() : string
|
||||
{
|
||||
return isset($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : "";
|
||||
}
|
||||
|
||||
protected function scheme() : string
|
||||
|
||||
protected function scheme() : string
|
||||
{
|
||||
return ( $this->isHttps() ? "https" : "http" ) . "://";
|
||||
}
|
||||
|
||||
protected function base() : string
|
||||
|
||||
protected function base() : string
|
||||
{
|
||||
return $this->urlBase ? $this->urlBase . "/" : "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if Uri's segments are valid
|
||||
* @param array $segments The uri's segments
|
||||
*/
|
||||
protected function secure($segments = []) : array
|
||||
protected function secure($segments = []) : array
|
||||
{
|
||||
return array_diff($segments, [ '..', '://' ]);
|
||||
}
|
||||
|
@ -95,25 +126,46 @@ class UrlExtension implements Extension {
|
|||
{
|
||||
return strtolower($_SERVER['HTTP_HOST']);
|
||||
}
|
||||
|
||||
protected function isDefaultPort() : bool
|
||||
|
||||
protected function isDefaultPort() : bool
|
||||
{
|
||||
return in_array($_SERVER['SERVER_PORT'], [ 80, 443 ]);
|
||||
}
|
||||
|
||||
protected function isHttps() : bool
|
||||
|
||||
protected function isHttps() : bool
|
||||
{
|
||||
$header = in_array('https', array_map("strtolower", [
|
||||
$header = in_array('https', array_map("strtolower", [
|
||||
$_SERVER['X-Url-Scheme'] ?? "",
|
||||
$_SERVER['Front-End-Https'] ?? "",
|
||||
$_SERVER['Front-End-Https'] ?? "",
|
||||
$_SERVER['X-Forwarded-Ssl'] ?? "",
|
||||
$_SERVER['X-Forwarded-Proto'] ?? "",
|
||||
$_SERVER['X-Forwarded-Proto'] ?? "",
|
||||
$_SERVER['X-Forwarded-Protocol'] ?? "",
|
||||
]));
|
||||
|
||||
return $header
|
||||
|| ( "443" === $_SERVER['SERVER_PORT'] ?? "" )
|
||||
|| ( "https" === $_SERVER['REQUEST_SCHEME'] ?? "http" )
|
||||
|
||||
return $header
|
||||
|| ( "443" === $_SERVER['SERVER_PORT'] ?? "" )
|
||||
|| ( "https" === $_SERVER['REQUEST_SCHEME'] ?? "http" )
|
||||
|| ( "off" !== strtolower($_SERVER['HTTPS'] ?? "off") );
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepareRoute(string $route, array &$arguments)
|
||||
{
|
||||
if ( preg_match_all('~{(.*?)}~si', $route, $matches, PREG_SET_ORDER) ) {
|
||||
$search = [];
|
||||
|
||||
foreach($matches as $item) {
|
||||
if ( ! array_key_exists($item[1], $arguments) ) {
|
||||
throw new \InvalidArgumentException("Argument `{$item[1]}` is missing within required route parameter(s)");
|
||||
}
|
||||
|
||||
$search[ $item[0] ] = $arguments[ $item[1] ];
|
||||
unset($arguments[ $item[1] ]);
|
||||
}
|
||||
|
||||
$route = str_replace(array_keys($search), array_values($search), $route);
|
||||
}
|
||||
|
||||
return $route;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,13 @@ class Picea implements LanguageRegistration
|
|||
public bool $debug;
|
||||
|
||||
public function __construct(
|
||||
?Closure $responseHtml = null,
|
||||
?Compiler\Context $context = null,
|
||||
?Caching\Cache $cache = null,
|
||||
?Compiler $compiler = null,
|
||||
?LanguageRegistration $languageRegistration = null,
|
||||
?FileFetcher $fileFetcher = null,
|
||||
?string $builderTemplatePath = null,
|
||||
? Closure $responseHtml = null,
|
||||
? Compiler\Context $context = null,
|
||||
? Caching\Cache $cache = null,
|
||||
? Compiler $compiler = null,
|
||||
? LanguageRegistration $languageRegistration = null,
|
||||
? FileFetcher $fileFetcher = null,
|
||||
? string $builderTemplatePath = null,
|
||||
bool $debug = false
|
||||
){
|
||||
$this->response = $responseHtml;
|
||||
|
@ -44,6 +44,9 @@ class Picea implements LanguageRegistration
|
|||
$this->compiler = $compiler ?? $this->instanciateCompiler();
|
||||
$this->fileFetcher = $fileFetcher ?? $this->instanciateFileFetcher();
|
||||
$this->debug = $debug;
|
||||
|
||||
#$this->context->exportFunctions();
|
||||
$this->renderContext($this->context);
|
||||
}
|
||||
|
||||
public function renderHtml(string $viewPath, array $variables = [], ?object $proxy = null) : string
|
||||
|
@ -55,21 +58,31 @@ class Picea implements LanguageRegistration
|
|||
return $object();
|
||||
}
|
||||
|
||||
public function outputHtml(string $viewPath, array $variables) : ResponseInterface
|
||||
public function renderContext(Compiler\Context $context) : object
|
||||
{
|
||||
if ( $this->response ?? false ) {
|
||||
if ( false === $content = $this->cache->handle($viewPath) ) {
|
||||
|
||||
}
|
||||
|
||||
$source = $this->compileSource($source ?? "test");
|
||||
$response = $this->response;
|
||||
return $response("abc");
|
||||
if ( null === $object = $this->contextFromCache($context) ) {
|
||||
throw new \RuntimeException("An error occured while trying to save a compiled template.");
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("No \Psr\Http\Message\ResponseInterface closure provided. Please provide one using the constructor or assigning it to the class variable `responseHtml`.");
|
||||
return $object;
|
||||
}
|
||||
|
||||
|
||||
#public function outputHtml(string $viewPath, array $variables) : ResponseInterface
|
||||
#{
|
||||
# if ( $this->response ?? false ) {
|
||||
# if ( false === $content = $this->cache->handle($viewPath) ) {
|
||||
#
|
||||
# }
|
||||
|
||||
# $source = $this->compileSource($source ?? "test");
|
||||
# $response = $this->response;
|
||||
# return $response("abc");
|
||||
# }
|
||||
|
||||
# throw new \InvalidArgumentException("No \Psr\Http\Message\ResponseInterface closure provided. Please provide one using the constructor or assigning it to the class variable `responseHtml`.");
|
||||
#}
|
||||
|
||||
public function compileSource(string $source) : array
|
||||
{
|
||||
$this->compiler->loadSourceCode($source);
|
||||
|
@ -94,7 +107,7 @@ class Picea implements LanguageRegistration
|
|||
$context = clone $this->context;
|
||||
$context->viewPath = $viewPath;
|
||||
$context->source = $this->compiler->compile($context);
|
||||
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
|
@ -129,6 +142,16 @@ class Picea implements LanguageRegistration
|
|||
return $this->cache->load($viewPath, $this, $variables, $proxy);
|
||||
}
|
||||
|
||||
public function contextFromCache(Compiler\Context $context) : ?object
|
||||
{
|
||||
if ( $this->debug || ! $this->cache->compiled( $context->cacheFilename() ) ) {
|
||||
$context = $this->compileContext($context);
|
||||
$this->cache->save($context);
|
||||
}
|
||||
|
||||
return $this->cache->load($context->cacheFilename(), $this);
|
||||
}
|
||||
|
||||
public function instanciateCompiler() : Compiler
|
||||
{
|
||||
return new Compiler($this->languageRegistration);
|
||||
|
@ -182,4 +205,16 @@ class Picea implements LanguageRegistration
|
|||
|
||||
return $context;
|
||||
}
|
||||
|
||||
protected function compileContext(Compiler\Context $context) : Compiler\Context
|
||||
{
|
||||
$tmpFolder = sys_get_temp_dir();
|
||||
$builder = $this->instanciateBuilder();
|
||||
$compiled = $this->compileSource("");
|
||||
$context->viewPath = $context->cacheFilename();
|
||||
$builder->build($context, "");
|
||||
$context->classPath = $tmpFolder . DIRECTORY_SEPARATOR . $context->cacheFilename() . ".php";
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue