diff --git a/src/Compiler.php b/src/Compiler.php index 640c672..f725e0f 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -113,9 +113,7 @@ class Compiler 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); - } + $callable = is_string($value) ? fn(...$args) => call_user_func_array([ $ext, $value ], $args) : null; $context->pushFunction(is_numeric($name) ? $value : $name, $callable ?? $value); } diff --git a/src/ControlStructure/BlockToken.php b/src/ControlStructure/BlockToken.php index 3ee235b..e369781 100644 --- a/src/ControlStructure/BlockToken.php +++ b/src/ControlStructure/BlockToken.php @@ -24,14 +24,13 @@ class BlockToken implements ControlStructure { return <<using ?? [], \EXTR_OVERWRITE); extract( \\$class::parseArguments(function($arguments) {}, \$inlineVariables), \EXTR_OVERWRITE); - unset(\$inlineVariables); } catch(\TypeError \$ex) { - throw new \Exception( - sprintf('A block awaiting arguments `%s` instead received `%s` with values `%s`', '$arguments', implode(', ', array_map('gettype', \$inlineVariables)), json_encode(\$inlineVariables)) + sprintf('A block awaiting arguments `%s` instead received `%s` with values `%s`', '$arguments', implode(', ', array_map('gettype', \$inlineVariables ?? [])), json_encode(\$inlineVariables)) ); } ?> @@ -72,7 +71,7 @@ class BlockToken implements ControlStructure { } return <<slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea) { extract(\$___using, \EXTR_SKIP); ?> + slotIsSet($name) || \$___block->setSlot($name, function($definition array \$___using = []) use (\$picea) { extract(\$___using, \EXTR_SKIP); ?> PHP; } @@ -131,6 +130,9 @@ class BlockToken implements ControlStructure { elseif ( $value->isVariadic() ) { $parameters[ $value->getName() ] = []; } + else { + $parameters[ $value->getName() ] = null; + } } return $parameters; @@ -217,7 +219,7 @@ class BlockToken implements ControlStructure { public function render(object $classTemplate) : string { $this->rendering = true; - + return $classTemplate->picea->inlineBlock($this, $this->viewPath, ...$this->arguments); } diff --git a/src/ControlStructure/FunctionToken.php b/src/ControlStructure/FunctionToken.php new file mode 100644 index 0000000..da82ea7 --- /dev/null +++ b/src/ControlStructure/FunctionToken.php @@ -0,0 +1,47 @@ +printFunction($context, $arguments); + + case "return": + if ( empty($context->functions) ) { + throw new \RuntimeException("A function return tag {% return %} was found without an opening {% function ... %} tag"); + } + + return $this->printReturn($context, $arguments); + + case "endfunction": + if ( empty($context->functions) ) { + throw new \RuntimeException("A function closing tag {% endfunction %} was found without an opening {% function ... %} tag"); + } + + $context->functions--; + + return $this->printEndFunction($context); + } + } + + protected function printFunction($context, ?string $arguments) : string + { + $context->functions++; + return ""; + } + + protected function printReturn($context, ?string $arguments) : string + { + return ""; + } + + protected function printEndFunction($context) : string + { + return ""; + } +} \ No newline at end of file diff --git a/src/ControlStructure/SectionToken.php b/src/ControlStructure/SectionToken.php index 08543ad..127521f 100644 --- a/src/ControlStructure/SectionToken.php +++ b/src/ControlStructure/SectionToken.php @@ -53,7 +53,7 @@ class SectionToken implements ControlStructure { protected function printEndSection($context) : string { $section = array_pop($context->sections); - $build = $context->extendFrom ? "\$___class__template->sectionStack && \$___class__template->renderSection({$section['name']});" : "\$___class__template->renderSection({$section['name']});"; + $build = $context->extendFrom ? "!empty(\$___class__template->sectionStack) && \$___class__template->renderSection({$section['name']});" : "\$___class__template->renderSection({$section['name']});"; return "sectionStack); }]; $build?>"; } } \ No newline at end of file diff --git a/src/Extension/JsonExtension.php b/src/Extension/JsonExtension.php index 32e447d..260dc2a 100644 --- a/src/Extension/JsonExtension.php +++ b/src/Extension/JsonExtension.php @@ -6,7 +6,7 @@ use Picea\Compiler\Context; class JsonExtension implements Extension, FunctionExtension { - public array $token = [ "json", "json.pretty" ]; + public array $token = [ "json", "json.pretty", "json.html" ]; public int $flags = JSON_HEX_TAG | \JSON_HEX_QUOT | \JSON_THROW_ON_ERROR | \JSON_UNESCAPED_UNICODE; @@ -23,16 +23,32 @@ class JsonExtension implements Extension, FunctionExtension { return "flags}), ENT_QUOTES, 'UTF-8') ?>"; } - return ""; + $cls = static::class; + + return ""; } public function exportFunctions(): array { return [ 'json' => function($arguments, ? int $flags = null) { - return json_encode($arguments, \JSON_FORCE_OBJECT); + return json_encode($arguments, \JSON_FORCE_OBJECT); }, ]; } + public static function utf8($src) { + + if (is_array($src)) { + foreach ($src as $key => $value) { + $src[$key] = static::utf8($value); + } + } + elseif (is_string($src)) { + return mb_convert_encoding($src, "UTF-8", "UTF-8"); + } + + return $src; + } + } diff --git a/src/Extension/LanguageExtension.php b/src/Extension/LanguageExtension.php index 8949b9a..8bd3f50 100644 --- a/src/Extension/LanguageExtension.php +++ b/src/Extension/LanguageExtension.php @@ -6,7 +6,7 @@ use Picea\Compiler\Context; class LanguageExtension implements Extension, FunctionExtension { - public array $tokens = [ "lang", "_", "language.set" ]; + public array $tokens = [ "lang", "lang.raw", "_", "_.raw", "language.set" ]; public string $currentLanguage = ""; @@ -31,9 +31,15 @@ class LanguageExtension implements Extension, FunctionExtension { return "compiler->getExtensionFromToken('$token')->currentLanguage = $arguments; ?>"; case "lang": + return "compiler->getExtensionFromToken('$token')->absoluteLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>"; + + case "lang.raw": return "compiler->getExtensionFromToken('$token')->absoluteLang($arguments) ?>"; case "_": + return "compiler->getExtensionFromToken('$token')->relativeLang($arguments), \ENT_QUOTES, ini_get('default_charset'), true) ?>"; + + case "_.raw": return "compiler->getExtensionFromToken('$token')->relativeLang($arguments) ?>"; } diff --git a/src/Extension/MoneyExtension.php b/src/Extension/MoneyExtension.php new file mode 100644 index 0000000..48c62c0 --- /dev/null +++ b/src/Extension/MoneyExtension.php @@ -0,0 +1,41 @@ +register($context); + $this->locale = explode('.', \Locale::getDefault())[0]; + $this->formatter = new \NumberFormatter($this->locale, \NumberFormatter::CURRENCY); + } + + public function register(Context $context) : void + { + $context->pushFunction("money", [ $this, 'money' ]); + } + + public function parse(/*\Picae\Compiler\Context*/ &$context, ?string $arguments, string $token) { + return ""; + } + + public function money(float $money, ? string $currency = null) /* : string|false */ + { + $this->formatter->setTextAttribute(\NumberFormatter::CURRENCY_CODE, 'CAD'); + + $this->formatter->setPattern( str_replace('¤#','¤ #', $this->formatter->getPattern() ) ); + + return $this->formatter->formatCurrency($money, $currency ?? $this->formatter->getTextAttribute(\NumberFormatter::CURRENCY_CODE)); + } + +} diff --git a/src/Extension/UrlExtension.php b/src/Extension/UrlExtension.php index fddab6e..0d3b07c 100644 --- a/src/Extension/UrlExtension.php +++ b/src/Extension/UrlExtension.php @@ -14,8 +14,8 @@ class UrlExtension implements Extension { protected array $routesTarget; - public array $tokens = [ "url" , "route", "route.cacheless", "asset", "url.current", "url.parameters" ]; - + public array $tokens = [ "url" , "route", "route.cacheless", "asset", "url.current", "url.parameters", "slug" ]; + public function __construct(Context $context, string $urlBase = "", string $assetToken = "") { $this->urlBase = trim($urlBase, "/"); $this->assetToken = $assetToken; @@ -39,6 +39,9 @@ class UrlExtension implements Extension { case "url.parameters": return "compiler->getExtensionFromToken('$token')->setUrlParameters($arguments) ?>"; + + case "slug": + return \Transliterator::createFromRules(':: Any-Latin;:: NFD;:: [:Nonspacing Mark:] Remove;:: NFC;:: [:Punctuation:] Remove;:: Lower();[:Separator:] > \'-\'')->transliterate( $arguments ); } return null; @@ -50,6 +53,7 @@ class UrlExtension implements Extension { $context->pushFunction("current_url", [ $this, 'currentUrl' ]); $context->pushFunction("asset", [ $this, 'buildAssetUrl' ]); $context->pushFunction("route", [ $this, 'buildRouteUrl' ]); + $context->pushFunction("slug", [ $this, 'slug' ]); } public function getRouteList(bool $full = false) : array @@ -96,6 +100,11 @@ class UrlExtension implements Extension { return $this->scheme() . $this->domain() . $this->base(); } + 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))); + } + public function registerRoute(string $name, string $route, string $class, string $method, array $routeMethods) : void { $this->routes[$name] = [ @@ -159,18 +168,19 @@ class UrlExtension implements Extension { protected function isHttps() : bool { - $header = in_array('https', array_map("strtolower", [ + $https = in_array('https', array_map("strtolower", [ + $_SERVER['REQUEST_SCHEME'] ?? "", $_SERVER['X-Url-Scheme'] ?? "", $_SERVER['Front-End-Https'] ?? "", - $_SERVER['X-Forwarded-Ssl'] ?? "", $_SERVER['X-Forwarded-Proto'] ?? "", $_SERVER['X-Forwarded-Protocol'] ?? "", - ])); + $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? "", + $_SERVER['HTTP_X_FORWARDED_PROTOCOL'] ?? "", + ])) || isset($_SERVER['HTTP_X_ARR_SSL']); - return $header + return $https || ( "443" === ( $_SERVER['SERVER_PORT'] ?? "" ) ) - || ( "https" === ( $_SERVER['REQUEST_SCHEME'] ?? "http") ) - || ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? "off")) ); + || ( "off" !== ( strtolower($_SERVER['HTTPS'] ?? $_SERVER['HTTP_X_FORWARDED_SSL'] ?? $_SERVER['X-Forwarded-Ssl'] ?? "off")) ); } protected function prepareRoute(string $route, array &$arguments) @@ -205,6 +215,5 @@ class UrlExtension implements Extension { } return $route; - } } diff --git a/src/Language/DefaultRegistrations.php b/src/Language/DefaultRegistrations.php index aea8a95..f23b938 100644 --- a/src/Language/DefaultRegistrations.php +++ b/src/Language/DefaultRegistrations.php @@ -50,6 +50,7 @@ class DefaultRegistrations implements LanguageRegistration $compiler->registerControlStructure(new \Picea\ControlStructure\BreakToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ExtendsToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\SectionToken()); + $compiler->registerControlStructure(new \Picea\ControlStructure\FunctionToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\BlockToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\IncludeToken()); $compiler->registerControlStructure(new \Picea\ControlStructure\ViewToken()); diff --git a/src/Method/Request.php b/src/Method/Request.php index cc591f7..1cfff22 100644 --- a/src/Method/Request.php +++ b/src/Method/Request.php @@ -7,6 +7,7 @@ use Picea\Extension\Extension, use Picea\Compiler\Context; +use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ServerRequestInterface; class Request implements Extension { @@ -46,7 +47,7 @@ class Request implements Extension { public function post(? string $variableName = null, $default = null) { - return $variableName === null ? $this->request->getParsedBody() : static::arrayGet($this->request->getParsedBody(), $variableName) ?? $default; + return $variableName === null ? $this->_post() : static::arrayGet($this->_post(), $variableName) ?? $default; } public function request(? string $variableName = null, $default = null) @@ -75,4 +76,19 @@ class Request implements Extension { return null; } } + + protected function _post() : array + { + $post = $this->request->getParsedBody(); + + if ( ! $post ) { + $content = utf8_encode((string) $this->request->getBody()); + + if ( $content && ( $json = json_decode($content, true) ) ) { + $post = $json; + } + } + + return $post ?: []; + } } \ No newline at end of file