diff --git a/src/flextype/core/Entries/Directives/ExpressionsDirective.php b/src/flextype/core/Entries/Directives/ExpressionsDirective.php index 140c712d..b550e39c 100644 --- a/src/flextype/core/Entries/Directives/ExpressionsDirective.php +++ b/src/flextype/core/Entries/Directives/ExpressionsDirective.php @@ -18,11 +18,11 @@ namespace Flextype\Entries\Directives; use function Flextype\emitter; use function Flextype\entries; -use function Flextype\expression; +use function Flextype\parsers; use function Flextype\registry; use function Flextype\collection; -// Directive: [[ ]] +// Directive: [[ ]] [% %] [# #] emitter()->addListener('onEntriesFetchSingleField', static function (): void { if (! registry()->get('flextype.settings.entries.directives.expressions.enabled')) { return; @@ -32,22 +32,15 @@ emitter()->addListener('onEntriesFetchSingleField', static function (): void { return; } - $selfQuote = fn ($text) => preg_replace('/(.)/us', '\\\\$0', $text); - $openingTag = registry()->get('flextype.settings.entries.directives.expressions.opening_tag'); - $closingTag = registry()->get('flextype.settings.entries.directives.expressions.closing_tag'); - $field = entries()->registry()->get('methods.fetch.field'); + // Convert entry fields to vars. + foreach (json_decode(json_encode((object) entries()->registry()->get('methods.fetch.result')), false) as $key => $value) { + $vars[$key] = $value; + } + if (is_string($field['value'])) { - $field['value'] = preg_replace_callback('/' . $selfQuote($openingTag) . ' (.*?) ' . $selfQuote($closingTag) . '/s', function ($matches) { - - // Prepare vars - foreach (json_decode(json_encode((object) entries()->registry()->get('methods.fetch.result')), false) as $key => $value) { - $vars[$key] = $value; - } - - return expression()->evaluate($matches[1], $vars); - }, $field['value']); + $field['value'] = parsers()->expressions()->parse($field['value'], $vars); } entries()->registry()->set('methods.fetch.field.key', $field['key']); diff --git a/src/flextype/core/Entries/Entries.php b/src/flextype/core/Entries/Entries.php index 74264e20..2b78e181 100755 --- a/src/flextype/core/Entries/Entries.php +++ b/src/flextype/core/Entries/Entries.php @@ -33,7 +33,7 @@ use function Flextype\serializers; use function Glowy\Strings\strings; use function Flextype\filterCollection; use function Flextype\collection; -use function Flextype\expression; +use function Flextype\parsers; class Entries { @@ -82,9 +82,8 @@ class Entries $this->setRegistry($registry); $this->setOptions($options); - $this->initExpressions(registry()->get('flextype.settings.entries.expressions')); - $this->initDirectives(registry()->get('flextype.settings.entries.directives')); - $this->initMacros(registry()->get('flextype.settings.entries.macros')); + $this->registerDirectives(registry()->get('flextype.settings.entries.directives')); + $this->registerMacros(registry()->get('flextype.settings.entries.macros')); $this->loadCollectionsEvents(); $this->loadCollectionsFields(); } @@ -100,16 +99,29 @@ class Entries */ private function removeSystemFields($data) { + $result = []; + if (is_array($data)) { - if (boolval(arrays($data)->get('macros.debug', registry()->get('flextype.settings.entries.macros.debug'))) === false) { - unset($data['macros']); - } - if (boolval(arrays($data)->get('vars.debug', registry()->get('flextype.settings.entries.vars.debug'))) === false) { - unset($data['vars']); + foreach ($data as $key => $value) { + + // remove hidden fields + if (strings($key)->startsWith('_')) { + continue; + } + + // remove macros fields + if ($key == 'macros') { + if (isset($value['debug']) && $value['debug'] == 'true') { + // display macros... + } else { + continue; + } + } + $result[$key] = $value; } } - return $data; + return $result; } /** @@ -119,7 +131,7 @@ class Entries * * @access public */ - public function initMacros(array $macros): void + public function registerMacros(array $macros): void { foreach ($macros as $key => $value) { if ($key == 'debug') { @@ -139,7 +151,7 @@ class Entries * * @access public */ - public function initDirectives(array $directives): void + public function registerDirectives(array $directives): void { foreach ($directives as $key => $value) { if (filesystem()->file(FLEXTYPE_ROOT_DIR . '/' . $value['path'])->exists()) { @@ -148,38 +160,6 @@ class Entries } } - /** - * Init Expressions - * - * @param array $expressions Expressions to init. - * - * @return void - */ - public function initExpressions(array $expressions): void - { - if (count($expressions) <= 0) { - return; - } - - foreach ($expressions as $expression) { - if (! isset($expression['enabled'])) { - continue; - } - - if (! $expression['enabled']) { - continue; - } - - if (! strings($expression['class'])->endsWith('Expression')) { - continue; - } - - if (class_exists($expression['class'])) { - expression()->registerProvider(new $expression['class']()); - } - } - } - /** * Load events for current collection. * diff --git a/src/flextype/core/Entries/Expressions/FieldExpression.php b/src/flextype/core/Entries/Expressions/FieldExpression.php deleted file mode 100644 index 740d0677..00000000 --- a/src/flextype/core/Entries/Expressions/FieldExpression.php +++ /dev/null @@ -1,30 +0,0 @@ - "\Flextype\entries()->registry()->get('methods.fetch.result.' . $field . ')'", static fn ($arguments, string $field) => entries()->registry()->get('methods.fetch.result.' . $field))]; - } -} diff --git a/src/flextype/core/Entries/Expressions/VarExpression.php b/src/flextype/core/Entries/Expressions/VarExpression.php deleted file mode 100644 index 26ec08ac..00000000 --- a/src/flextype/core/Entries/Expressions/VarExpression.php +++ /dev/null @@ -1,84 +0,0 @@ -stripQuotes()->toString(); - $code = "\Flextype\\entries()->registry()->get('methods.fetch.result.$var');"; - return $code; - }, - static function ($arguments, string $var) { - return entries()->registry()->get('methods.fetch.result.vars.' . $var); - } - ), - new ExpressionFunction('set', - static function (string $var, $value) { - $var = strings($var)->stripQuotes()->toString(); - $value = strings($value)->stripQuotes()->toString(); - $code = "\Flextype\\entries()->registry()->set('methods.fetch.result.$var', '$value');"; - return $code; - }, - static function ($arguments, string $var, $value) { - entries()->registry()->set('methods.fetch.result.' . $var, $value); - } - ), - new ExpressionFunction('get', - static function (string $var, $default = '') { - $var = strings($var)->stripQuotes()->toString(); - $default = strings($default)->stripQuotes()->toString(); - $code = "\Flextype\\entries()->registry()->get('methods.fetch.result.$var'" . ($default ? ", '$default'" : '') . ");"; - return $code; - }, - static function ($arguments, string $var, $default = '') { - return entries()->registry()->get('methods.fetch.result.' . $var, $default); - } - ), - new ExpressionFunction('delete', - static function (string $var) { - $var = strings($var)->stripQuotes()->toString(); - $code = "\Flextype\\entries()->registry()->delete('methods.fetch.result.$var');"; - return $code; - }, - static function ($arguments, string $var) { - entries()->registry()->delete('methods.fetch.result.' . $var); - } - ), - new ExpressionFunction('unset', - static function (string $var) { - $var = strings($var)->stripQuotes()->toString(); - $code = "\Flextype\\entries()->registry()->set('methods.fetch.result.$var', null);"; - return $code; - }, - static function ($arguments, string $var) { - entries()->registry()->set('methods.fetch.result.' . $var, null); - } - ), - ]; - } -} diff --git a/src/flextype/core/Parsers/Expressions.php b/src/flextype/core/Parsers/Expressions.php new file mode 100644 index 00000000..03f1c77d --- /dev/null +++ b/src/flextype/core/Parsers/Expressions.php @@ -0,0 +1,277 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flextype\Parsers; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\Lexer; +use Symfony\Component\ExpressionLanguage\Parser; +use Symfony\Component\ExpressionLanguage\Compiler; +use Symfony\Component\ExpressionLanguage\ParsedExpression; + +use function Glowy\Strings\strings; +use function Flextype\registry; +use function Flextype\cache; +use function Flextype\entries; + +// Help opcache.preload discover always-needed symbols +class_exists(ParsedExpression::class); + +final class Expressions +{ + /** + * Expressions instance. + */ + private static ?Expressions $instance = null; + + private Lexer $lexer; + private Parser $parser; + private Compiler $compiler; + + protected array $functions = []; + + + /** + * @param ExpressionFunctionProviderInterface[] $providers + */ + protected function __construct() + { + // Register default php functions + $this->addFunction(ExpressionFunction::fromPhp('constant')); + + // Register the expressions providers + $this->registerExpressions(registry()->get('flextype.settings.parsers.expressions.expressions')); + } + + /** + * Expressions should not be cloneable. + */ + protected function __clone() + { + throw new Exception('Cannot clone a Expressions.'); + } + + /** + * Expressions should not be restorable from strings. + */ + public function __wakeup(): void + { + throw new Exception('Cannot unserialize a Expressions.'); + } + + /** + * Gets the instance via lazy initialization (created on first usage). + */ + public static function getInstance(): Expressions + { + if (static::$instance === null) { + static::$instance = new self(); + } + + return static::$instance; + } + + /** + * Compiles an expression source code. + */ + public function compile(Expression|string $expression, array $names = []): string + { + return $this->getCompiler()->compile($this->parseExpression($expression, $names)->getNodes())->getSource(); + } + + /** + * Evaluate an expression. + */ + public function eval(Expression|string $expression, array $values = []): mixed + { + return $this->parseExpression($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values); + } + + /** + * Fallback method to evaluate an expression. + */ + public function evaluate(Expression|string $expression, array $values = []): mixed + { + return $this->eval($expression, $values); + } + + /** + * Parses text to evaluate or compile expressions. + */ + public function parse(Expression|string $string, array $values = [], bool $compile = false) + { + if ($string instanceof Expression) { + return $string; + } + + $selfQuote = fn ($string) => preg_replace('/(.)/us', '\\\\$0', $string); + $openingVariableTag = registry()->get('flextype.settings.parsers.expressions.opening_variable_tag'); + $closingVariableTag = registry()->get('flextype.settings.parsers.expressions.closing_variable_tag'); + $openingBlockTag = registry()->get('flextype.settings.parsers.expressions.opening_block_tag'); + $closingBlockTag = registry()->get('flextype.settings.parsers.expressions.closing_block_tag'); + $openingCommentTag = registry()->get('flextype.settings.parsers.expressions.opening_comment_tag'); + $closingCommentTag = registry()->get('flextype.settings.parsers.expressions.closing_comment_tag'); + + // [# #] - comments + $string = preg_replace_callback('/' . $selfQuote($openingCommentTag) . ' (.*?) ' . $selfQuote($closingCommentTag) . '/sx', function ($matches) use ($values, $compile, $string) { + return ''; + }, $string); + + // [% %] - blocks + $string = preg_replace_callback('/' . $selfQuote($openingBlockTag) . ' (.*?) ' . $selfQuote($closingBlockTag) . '/sx', function ($matches) use ($values, $compile, $string) { + $this->{$compile ? 'compile' : 'eval'}($matches[1], $values); + return ''; + }, $string); + + // [[ ]] - variables + $string = preg_replace_callback('/' . $selfQuote($openingVariableTag) . ' (.*?) ' . $selfQuote($closingVariableTag) . '/sx', function ($matches) use ($values, $compile) { + return $this->{$compile ? 'compile' : 'eval'}($matches[1], $values); + }, $string); + + return $string; + } + + /** + * Parses an expression. + */ + public function parseExpression(Expression|string $expression, array $names): ParsedExpression + { + if ($expression instanceof ParsedExpression) { + return $expression; + } + + if (registry()->get('flextype.settings.parsers.expressions.cache.enabled') === true && + registry()->get('flextype.settings.cache.enabled') === true) { + + $cacheItem = $this->getExpressionCacheID($expression, $names); + + if (! cache()->has($cacheItem)) { + $parsedExpression = new ParsedExpression((string) $expression, $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names)); + cache()->set($cacheItem, $parsedExpression); + return $parsedExpression; + } else { + return cache()->get($cacheItem); + } + } + + return new ParsedExpression((string) $expression, $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names)); + } + + /** + * Validates the syntax of an expression. + * + * @param array|null $names The list of acceptable variable names in the expression, or null to accept any names + * + * @throws SyntaxError When the passed expression is invalid + */ + public function lint(Expression|string $expression, ?array $names): void + { + if ($expression instanceof ParsedExpression) { + return; + } + + $this->getParser()->lint($this->getLexer()->tokenize((string) $expression), $names); + } + + /** + * Registers a function. + * + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + * + * @throws \LogicException when registering a function after calling evaluate(), compile() or parse() + * + * @see ExpressionFunction + */ + public function register(string $name, callable $compiler, callable $evaluator) + { + if (isset($this->parser)) { + throw new \LogicException('Registering functions after calling evaluate(), compile() or parse() is not supported.'); + } + + $this->functions[$name] = ['compiler' => $compiler, 'evaluator' => $evaluator]; + } + + public function addFunction(ExpressionFunction $function) + { + $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator()); + } + + public function registerProvider($provider) + { + foreach ($provider->getFunctions() as $function) { + $this->addFunction($function); + } + } + + public function registerExpressions(array $expressions) { + + if (count($expressions) >= 0) { + + foreach ($expressions as $expression) { + if (! isset($expression['enabled'])) { + continue; + } + + if (! $expression['enabled']) { + continue; + } + + if (! strings($expression['class'])->endsWith('Expression')) { + continue; + } + + if (class_exists($expression['class'])) { + $this->registerProvider(new $expression['class']()); + } + } + } + } + + private function getLexer(): Lexer + { + return $this->lexer ??= new Lexer(); + } + + private function getParser(): Parser + { + return $this->parser ??= new Parser($this->functions); + } + + private function getCompiler(): Compiler + { + $this->compiler ??= new Compiler($this->functions); + + return $this->compiler->reset(); + } + + public function getExpressionCacheID(Expression|string $expression, array $names, string $string = ''): string + { + if ($expression instanceof ParsedExpression) { + return ''; + } + + // Go through... + asort($names); + + $cacheKeyItems = []; + + foreach ($names as $nameKey => $name) { + $cacheKeyItems[] = \is_int($nameKey) ? $name : $nameKey.':'.$name; + } + + // Create Unique Cache ID for Expression + return md5('expression' . $string . rawurlencode($expression.'//'.implode('|', $cacheKeyItems))); + } +} \ No newline at end of file diff --git a/src/flextype/core/Entries/Expressions/ActionsExpression.php b/src/flextype/core/Parsers/Expressions/ActionsExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/ActionsExpression.php rename to src/flextype/core/Parsers/Expressions/ActionsExpression.php index 60d55125..fabd8f90 100644 --- a/src/flextype/core/Entries/Expressions/ActionsExpression.php +++ b/src/flextype/core/Parsers/Expressions/ActionsExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/CollectionExpression.php b/src/flextype/core/Parsers/Expressions/CollectionExpression.php similarity index 98% rename from src/flextype/core/Entries/Expressions/CollectionExpression.php rename to src/flextype/core/Parsers/Expressions/CollectionExpression.php index bd24b624..aaea9424 100644 --- a/src/flextype/core/Entries/Expressions/CollectionExpression.php +++ b/src/flextype/core/Parsers/Expressions/CollectionExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/ConstExpression.php b/src/flextype/core/Parsers/Expressions/ConstExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/ConstExpression.php rename to src/flextype/core/Parsers/Expressions/ConstExpression.php index 7e02cf33..df3febaa 100644 --- a/src/flextype/core/Entries/Expressions/ConstExpression.php +++ b/src/flextype/core/Parsers/Expressions/ConstExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/CsrfExpression.php b/src/flextype/core/Parsers/Expressions/CsrfExpression.php similarity index 96% rename from src/flextype/core/Entries/Expressions/CsrfExpression.php rename to src/flextype/core/Parsers/Expressions/CsrfExpression.php index 49ae5946..d5f1e5a2 100644 --- a/src/flextype/core/Entries/Expressions/CsrfExpression.php +++ b/src/flextype/core/Parsers/Expressions/CsrfExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/DateExpression.php b/src/flextype/core/Parsers/Expressions/DateExpression.php similarity index 96% rename from src/flextype/core/Entries/Expressions/DateExpression.php rename to src/flextype/core/Parsers/Expressions/DateExpression.php index fba47949..bb4be2f0 100644 --- a/src/flextype/core/Entries/Expressions/DateExpression.php +++ b/src/flextype/core/Parsers/Expressions/DateExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/EntriesExpression.php b/src/flextype/core/Parsers/Expressions/EntriesExpression.php similarity index 82% rename from src/flextype/core/Entries/Expressions/EntriesExpression.php rename to src/flextype/core/Parsers/Expressions/EntriesExpression.php index c159feae..991001bc 100644 --- a/src/flextype/core/Entries/Expressions/EntriesExpression.php +++ b/src/flextype/core/Parsers/Expressions/EntriesExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Glowy\Arrays\Arrays; use Glowy\Macroable\Macroable; @@ -49,7 +49,7 @@ class EntriesExpressionsMethods */ public function fetch(string $id, array $options = []): \Glowy\Arrays\Arrays { - if (! registry()->get('flextype.settings.entries.expressions.entries.fetch.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.fetch.enabled')) { return collection(); } @@ -74,7 +74,7 @@ class EntriesExpressionsMethods */ public function registry(): \Glowy\Arrays\Arrays { - if (! registry()->get('flextype.settings.entries.expressions.entries.registry.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.registry.enabled')) { return collection(); } @@ -92,7 +92,7 @@ class EntriesExpressionsMethods */ public function has(string $id): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.has.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.has.enabled')) { return false; } @@ -111,7 +111,7 @@ class EntriesExpressionsMethods */ public function move(string $id, string $newID): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.move.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.move.enabled')) { return false; } @@ -130,7 +130,7 @@ class EntriesExpressionsMethods */ public function update(string $id, array $data): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.update.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.update.enabled')) { return false; } @@ -149,7 +149,7 @@ class EntriesExpressionsMethods */ public function create(string $id, array $data = []): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.create.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.create.enabled')) { return false; } @@ -167,7 +167,7 @@ class EntriesExpressionsMethods */ public function delete(string $id): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.delete.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.delete.enabled')) { return false; } @@ -186,7 +186,7 @@ class EntriesExpressionsMethods */ public function copy(string $id, string $newID): bool { - if (! registry()->get('flextype.settings.entries.expressions.entries.copy.enabled')) { + if (! registry()->get('flextype.settings.parsers.expressions.expressions.entries.copy.enabled')) { return false; } diff --git a/src/flextype/core/Entries/Expressions/FetchExpression.php b/src/flextype/core/Parsers/Expressions/FetchExpression.php similarity index 91% rename from src/flextype/core/Entries/Expressions/FetchExpression.php rename to src/flextype/core/Parsers/Expressions/FetchExpression.php index cf6fadbc..0c111f83 100644 --- a/src/flextype/core/Entries/Expressions/FetchExpression.php +++ b/src/flextype/core/Parsers/Expressions/FetchExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Glowy\Macroable\Macroable; use Symfony\Component\ExpressionLanguage\ExpressionFunction; @@ -26,7 +26,7 @@ class FetchExpression implements ExpressionFunctionProviderInterface { public function getFunctions() { - return [new ExpressionFunction('fetch', static fn (string $resource, array $options = []) => '(new \Flextype\Entries\Expressions\FetchExpressionsMethods())->fetch($resource, $options);', static fn ($arguments, string $resource, array $options = []) => (new \Flextype\Entries\Expressions\FetchExpressionsMethods())->fetch($resource, $options))]; + return [new ExpressionFunction('fetch', static fn (string $resource, array $options = []) => '(new \Flextype\Parsers\Expressions\FetchExpressionsMethods())->fetch($resource, $options);', static fn ($arguments, string $resource, array $options = []) => (new \Flextype\Parsers\Expressions\FetchExpressionsMethods())->fetch($resource, $options))]; } } diff --git a/src/flextype/core/Parsers/Expressions/FieldExpression.php b/src/flextype/core/Parsers/Expressions/FieldExpression.php new file mode 100644 index 00000000..c9d828c3 --- /dev/null +++ b/src/flextype/core/Parsers/Expressions/FieldExpression.php @@ -0,0 +1,70 @@ +stripQuotes()->toString(); + $code = "\Flextype\\entries()->registry()->get('methods.fetch.result.$field');"; + return $code; + }, + static fn ($arguments, string $field) => entries()->registry()->get('methods.fetch.result.' . $field) + ), + new ExpressionFunction('fields', + static fn () => "(new \\Flextype\\Parsers\\Expressions\\FieldsExpressionMethods())", + static fn ($arguments) => (new FieldsExpressionMethods()) + ) + ]; + } +} + +class FieldsExpressionMethods +{ + use Macroable; + + public function set(string|null $key, $value) + { + entries()->registry()->set('methods.fetch.result.' . $key, $value); + } + + public function get($key, $default = null) + { + return entries()->registry()->get('methods.fetch.result.' . $key, $default); + } + + public function unset(string|null $key) + { + entries()->registry()->set('methods.fetch.result.' . $key, null); + } + + public function delete($key) + { + return entries()->registry()->delete('methods.fetch.result.' . $key); + } +} \ No newline at end of file diff --git a/src/flextype/core/Entries/Expressions/FilesystemExpression.php b/src/flextype/core/Parsers/Expressions/FilesystemExpression.php similarity index 99% rename from src/flextype/core/Entries/Expressions/FilesystemExpression.php rename to src/flextype/core/Parsers/Expressions/FilesystemExpression.php index f1e90316..080818a8 100644 --- a/src/flextype/core/Entries/Expressions/FilesystemExpression.php +++ b/src/flextype/core/Parsers/Expressions/FilesystemExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Glowy\Macroable\Macroable; use Symfony\Component\ExpressionLanguage\ExpressionFunction; diff --git a/src/flextype/core/Entries/Expressions/I18nExpression.php b/src/flextype/core/Parsers/Expressions/I18nExpression.php similarity index 97% rename from src/flextype/core/Entries/Expressions/I18nExpression.php rename to src/flextype/core/Parsers/Expressions/I18nExpression.php index 6ccd4e14..48d4d9c0 100644 --- a/src/flextype/core/Entries/Expressions/I18nExpression.php +++ b/src/flextype/core/Parsers/Expressions/I18nExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/MathExpression.php b/src/flextype/core/Parsers/Expressions/MathExpression.php similarity index 97% rename from src/flextype/core/Entries/Expressions/MathExpression.php rename to src/flextype/core/Parsers/Expressions/MathExpression.php index e3033a67..fc93bc0b 100644 --- a/src/flextype/core/Entries/Expressions/MathExpression.php +++ b/src/flextype/core/Parsers/Expressions/MathExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/ParsersExpression.php b/src/flextype/core/Parsers/Expressions/ParsersExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/ParsersExpression.php rename to src/flextype/core/Parsers/Expressions/ParsersExpression.php index a4ee9f6e..ff0cffbb 100644 --- a/src/flextype/core/Entries/Expressions/ParsersExpression.php +++ b/src/flextype/core/Parsers/Expressions/ParsersExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/RegistryExpression.php b/src/flextype/core/Parsers/Expressions/RegistryExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/RegistryExpression.php rename to src/flextype/core/Parsers/Expressions/RegistryExpression.php index fecb82f9..427f98cb 100644 --- a/src/flextype/core/Entries/Expressions/RegistryExpression.php +++ b/src/flextype/core/Parsers/Expressions/RegistryExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/SerializersExpression.php b/src/flextype/core/Parsers/Expressions/SerializersExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/SerializersExpression.php rename to src/flextype/core/Parsers/Expressions/SerializersExpression.php index 592cd960..085177df 100644 --- a/src/flextype/core/Entries/Expressions/SerializersExpression.php +++ b/src/flextype/core/Parsers/Expressions/SerializersExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/SlugifyExpression.php b/src/flextype/core/Parsers/Expressions/SlugifyExpression.php similarity index 95% rename from src/flextype/core/Entries/Expressions/SlugifyExpression.php rename to src/flextype/core/Parsers/Expressions/SlugifyExpression.php index 3dde430a..9ba2290d 100644 --- a/src/flextype/core/Entries/Expressions/SlugifyExpression.php +++ b/src/flextype/core/Parsers/Expressions/SlugifyExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/StringsExpression.php b/src/flextype/core/Parsers/Expressions/StringsExpression.php similarity index 96% rename from src/flextype/core/Entries/Expressions/StringsExpression.php rename to src/flextype/core/Parsers/Expressions/StringsExpression.php index 4a711046..4a5dbec2 100644 --- a/src/flextype/core/Entries/Expressions/StringsExpression.php +++ b/src/flextype/core/Parsers/Expressions/StringsExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; diff --git a/src/flextype/core/Entries/Expressions/UrlExpression.php b/src/flextype/core/Parsers/Expressions/UrlExpression.php similarity index 98% rename from src/flextype/core/Entries/Expressions/UrlExpression.php rename to src/flextype/core/Parsers/Expressions/UrlExpression.php index 652b988e..d80b73de 100644 --- a/src/flextype/core/Entries/Expressions/UrlExpression.php +++ b/src/flextype/core/Parsers/Expressions/UrlExpression.php @@ -14,7 +14,7 @@ declare(strict_types=1); * Redistributions of files must retain the above copyright notice. */ -namespace Flextype\Entries\Expressions; +namespace Flextype\Parsers\Expressions; use Psr\Http\Message\ServerRequestInterface; use Symfony\Component\ExpressionLanguage\ExpressionFunction; diff --git a/src/flextype/core/Parsers/Expressions/VarExpression.php b/src/flextype/core/Parsers/Expressions/VarExpression.php new file mode 100644 index 00000000..a454f9ca --- /dev/null +++ b/src/flextype/core/Parsers/Expressions/VarExpression.php @@ -0,0 +1,44 @@ +stripQuotes()->toString(); + $code = "\Flextype\\vars()->get('$name');"; + return $code; + }, + static fn ($arguments, string $name) => vars()->get($name) + ), + new ExpressionFunction('vars', + static fn () => "\\Flextype\\vars()", + static fn ($arguments) => vars() + ) + ]; + } +} \ No newline at end of file diff --git a/src/flextype/core/Parsers/Parsers.php b/src/flextype/core/Parsers/Parsers.php index b9700bbe..0d115849 100644 --- a/src/flextype/core/Parsers/Parsers.php +++ b/src/flextype/core/Parsers/Parsers.php @@ -23,7 +23,7 @@ class Parsers use Macroable; /** - * Create a Shortcodes instance. + * Get a Shortcodes instance. */ public function shortcodes(): Shortcodes { @@ -31,7 +31,7 @@ class Parsers } /** - * Create a Markdown instance. + * Get a Markdown instance. */ public function markdown(): Markdown { @@ -39,10 +39,18 @@ class Parsers } /** - * Create a Textile instance. + * Get a Textile instance. */ public function textile(): Textile { return Textile::getInstance(); } + + /** + * Get a Expressions instance. + */ + public function expressions(): Expressions + { + return Expressions::getInstance(); + } } diff --git a/src/flextype/core/Parsers/Shortcodes/CalcShortcode.php b/src/flextype/core/Parsers/Shortcodes/CalcShortcode.php index 070be7aa..0cd7f944 100644 --- a/src/flextype/core/Parsers/Shortcodes/CalcShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/CalcShortcode.php @@ -29,5 +29,5 @@ parsers()->shortcodes()->addHandler('calc', static function (ShortcodeInterface return ''; } - return expression()->evaluate(parsers()->shortcodes()->parse($s->getBBCode())); + return parsers()->expressions()->eval(parsers()->shortcodes()->parse($s->getBBCode())); }); diff --git a/src/flextype/core/Parsers/Shortcodes/EvalShortcode.php b/src/flextype/core/Parsers/Shortcodes/EvalShortcode.php index 76a563cf..9bbeabef 100644 --- a/src/flextype/core/Parsers/Shortcodes/EvalShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/EvalShortcode.php @@ -32,10 +32,15 @@ parsers()->shortcodes()->addHandler('eval', static function (ShortcodeInterface } if ($s->getContent() !== null) { - return expression()->evaluate(parsers()->shortcodes()->parse($s->getContent())); + return parsers()->expressions()->eval(parsers()->shortcodes()->parse($s->getContent())); } if ($s->getBbCode() !== null) { - return expression()->evaluate(parsers()->shortcodes()->parse($s->getBBCode())); + return parsers()->expressions()->eval(parsers()->shortcodes()->parse($s->getBBCode())); } }); + +parsers()->shortcodes()->addHandler('compile', static function (ShortcodeInterface $s) { + return expression()->compile(parsers()->shortcodes()->parse($s->getContent())); +}); + diff --git a/src/flextype/core/Parsers/Shortcodes/FieldShortcode.php b/src/flextype/core/Parsers/Shortcodes/FieldShortcode.php index c964b0b1..9fb560c7 100644 --- a/src/flextype/core/Parsers/Shortcodes/FieldShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/FieldShortcode.php @@ -24,10 +24,53 @@ use function Flextype\registry; // Shortcode: field // Usage: (field:title) +// (field get:foo default:Foo) +// (field get:foo) Default (/field) +// (field set:foo value:Foo) +// (field set:foo) Foo (/field) +// (field unset:foo) +// (field delete:foo) parsers()->shortcodes()->addHandler('field', static function (ShortcodeInterface $s) { if (! registry()->get('flextype.settings.parsers.shortcodes.shortcodes.field.enabled')) { return ''; } + + $params = $s->getParameters(); - return entries()->registry()->get('methods.fetch.result.' . parsers()->shortcodes()->parse($s->getBBCode())); + // set + if (isset($params['set'])) { + if (isset($params['value'])) { + $value = $params['value']; + } else { + $value = $s->getContent() ?? ''; + } + + entries()->registry()->set('methods.fetch.result.' . parsers()->shortcodes()->parse($params['set']), parsers()->shortcodes()->parse($value)); + + return ''; + } + + // get + if (isset($params['get'])) { + $default = isset($params['default']) ? $params['default'] : $s->getContent() ?? ''; + return entries()->registry()->get('methods.fetch.result.' . parsers()->shortcodes()->parse($params['get']), $default); + } + + if ($s->getBBCode() !== null) { + return entries()->registry()->get('methods.fetch.result.' . parsers()->shortcodes()->parse($s->getBBCode())); + } + + // unset + if (isset($params['unset'])) { + entries()->registry()->set('methods.fetch.result.' . parsers()->shortcodes()->parse($params['unset']), null); + return ''; + } + + // delete + if (isset($params['delete'])) { + entries()->registry()->delete('methods.fetch.result.' . parsers()->shortcodes()->parse($params['delete'])); + return ''; + } + + return ''; }); diff --git a/src/flextype/core/Parsers/Shortcodes/IfShortcode.php b/src/flextype/core/Parsers/Shortcodes/IfShortcode.php index cb11643b..23c17c53 100644 --- a/src/flextype/core/Parsers/Shortcodes/IfShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/IfShortcode.php @@ -29,5 +29,5 @@ parsers()->shortcodes()->addHandler('if', static function (ShortcodeInterface $s return ''; } - return expression()->evaluate(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === true ? parsers()->shortcodes()->parse($s->getContent()) : ''; + return parsers()->expressions()->eval(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === true ? parsers()->shortcodes()->parse($s->getContent()) : ''; }); diff --git a/src/flextype/core/Parsers/Shortcodes/PhpShortcode.php b/src/flextype/core/Parsers/Shortcodes/PhpShortcode.php index 17b072b9..41b6c1c6 100644 --- a/src/flextype/core/Parsers/Shortcodes/PhpShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/PhpShortcode.php @@ -34,7 +34,6 @@ parsers()->shortcodes()->addHandler('php', static function (ShortcodeInterface $ if ($s->getContent() !== null) { ob_start(); eval($s->getContent()); - return ob_get_clean(); } diff --git a/src/flextype/core/Parsers/Shortcodes/UnlessShortcode.php b/src/flextype/core/Parsers/Shortcodes/UnlessShortcode.php index 20907256..58890e4f 100644 --- a/src/flextype/core/Parsers/Shortcodes/UnlessShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/UnlessShortcode.php @@ -29,5 +29,5 @@ parsers()->shortcodes()->addHandler('unless', static function (ShortcodeInterfac return ''; } - return expression()->evaluate(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === false ? parsers()->shortcodes()->parse($s->getContent()) : ''; + return parsers()->expressions()->eval(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === false ? parsers()->shortcodes()->parse($s->getContent()) : ''; }); diff --git a/src/flextype/core/Parsers/Shortcodes/VarShortcode.php b/src/flextype/core/Parsers/Shortcodes/VarShortcode.php index 7726ffde..d5c7c3f5 100644 --- a/src/flextype/core/Parsers/Shortcodes/VarShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/VarShortcode.php @@ -21,6 +21,7 @@ use Thunder\Shortcode\Shortcode\ShortcodeInterface; use function Flextype\entries; use function Flextype\parsers; use function Flextype\registry; +use function Flextype\vars; // Shortcode: var // Usage: (var:foo) @@ -31,9 +32,10 @@ parsers()->shortcodes()->addHandler('var', static function (ShortcodeInterface $ if (! registry()->get('flextype.settings.parsers.shortcodes.shortcodes.var.enabled')) { return ''; } - + $params = $s->getParameters(); + // set if (isset($params['set'])) { if (isset($params['value'])) { $value = $params['value']; @@ -41,18 +43,32 @@ parsers()->shortcodes()->addHandler('var', static function (ShortcodeInterface $ $value = $s->getContent() ?? ''; } - entries()->registry()->set('methods.fetch.result.vars.' . parsers()->shortcodes()->parse($params['set']), parsers()->shortcodes()->parse($value)); + vars()->set(parsers()->shortcodes()->parse($params['set']), parsers()->shortcodes()->parse($value)); return ''; } + // get if (isset($params['get'])) { - return entries()->registry()->get('methods.fetch.result.vars.' . parsers()->shortcodes()->parse($params['get'])); + $default = isset($params['default']) ? $params['default'] : $s->getContent() ?? ''; + return vars()->get(parsers()->shortcodes()->parse($params['get']), $default); } if ($s->getBBCode() !== null) { - return entries()->registry()->get('methods.fetch.result.vars.' . parsers()->shortcodes()->parse($s->getBBCode())); + return vars()->get(parsers()->shortcodes()->parse($s->getBBCode())); } + // unset + if (isset($params['unset'])) { + vars()->set(parsers()->shortcodes()->parse($params['unset']), null); + return ''; + } + + // delete + if (isset($params['delete'])) { + vars()->delete(parsers()->shortcodes()->parse($params['delete'])); + return ''; + } + return ''; }); diff --git a/src/flextype/core/Parsers/Shortcodes/WhenShortcode.php b/src/flextype/core/Parsers/Shortcodes/WhenShortcode.php index 1abcb687..a1e7b770 100644 --- a/src/flextype/core/Parsers/Shortcodes/WhenShortcode.php +++ b/src/flextype/core/Parsers/Shortcodes/WhenShortcode.php @@ -29,5 +29,5 @@ parsers()->shortcodes()->addHandler('when', static function (ShortcodeInterface return ''; } - return expression()->evaluate(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === true ? parsers()->shortcodes()->parse($s->getContent()) : ''; + return parsers()->expressions()->eval(parsers()->shortcodes()->parse(($s->getBbCode() ?? ''))) === true ? parsers()->shortcodes()->parse($s->getContent()) : ''; }); diff --git a/src/flextype/flextype.php b/src/flextype/flextype.php index 4848696b..e0ad0c42 100644 --- a/src/flextype/flextype.php +++ b/src/flextype/flextype.php @@ -26,6 +26,7 @@ use Flextype\Serializers\Serializers; use Glowy\Csrf\Csrf; use Glowy\Session\Session; use Glowy\View\View; +use Glowy\Arrays\Arrays; use League\Event\Emitter; use Monolog\Handler\StreamHandler; use Monolog\Logger; @@ -39,7 +40,6 @@ use Slim\Middleware\ContentLengthMiddleware; use Slim\Middleware\OutputBufferingMiddleware; use Slim\Middleware\RoutingMiddleware; use Slim\Psr7\Factory\StreamFactory; -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Yaml\Yaml as SymfonyYaml; use function array_replace_recursive; @@ -87,6 +87,9 @@ container()->set('registry', registry()); // Add Actions Service. container()->set('actions', Actions::getInstance()); +// Add Vars Service. +container()->set('vars', Vars::getInstance()); + // Init Flextype config (manifest and settings) $flextypeManifestFilePath = FLEXTYPE_ROOT_DIR . '/src/flextype/flextype.yaml'; $defaultFlextypeSettingsFilePath = FLEXTYPE_ROOT_DIR . '/src/flextype/settings.yaml'; @@ -189,9 +192,6 @@ if (registry()->get('flextype.settings.router.cache.enabled')) { app()->getRouteCollector()->setCacheFile(FLEXTYPE_PATH_TMP . '/routes/routes.php'); } -// Add Expression Service -container()->set('expression', new ExpressionLanguage()); - // Add Session Service container()->set('session', new Session()); diff --git a/src/flextype/settings.yaml b/src/flextype/settings.yaml index ba593b5a..c49ffc5c 100644 --- a/src/flextype/settings.yaml +++ b/src/flextype/settings.yaml @@ -76,84 +76,11 @@ entries: directory: 'entries' cache: string: "" - vars: - debug: false - expressions: - math: - enabled: true - class: "Flextype\\Entries\\Expressions\\MathExpression" - date: - enabled: true - class: "Flextype\\Entries\\Expressions\\DateExpression" - actions: - enabled: true - class: "Flextype\\Entries\\Expressions\\ActionsExpression" - registry: - enabled: true - class: "Flextype\\Entries\\Expressions\\RegistryExpression" - entries: - enabled: true - class: "Flextype\\Entries\\Expressions\\EntriesExpression" - fetch: - enabled: true - has: - enabled: true - registry: - enabled: true - create: - enabled: false - move: - enabled: false - update: - enabled: false - delete: - enabled: false - fetch: - enabled: true - class: "Flextype\\Entries\\Expressions\\FetchExpression" - filesystem: - enabled: true - class: "Flextype\\Entries\\Expressions\\FilesystemExpression" - i18n: - enabled: true - class: "Flextype\\Entries\\Expressions\\I18nExpression" - serializers: - enabled: true - class: "Flextype\\Entries\\Expressions\\SerializersExpression" - parsers: - enabled: true - class: "Flextype\\Entries\\Expressions\\ParsersExpression" - slugify: - enabled: true - class: "Flextype\\Entries\\Expressions\\SlugifyExpression" - strings: - enabled: true - class: "Flextype\\Entries\\Expressions\\StringsExpression" - collection: - enabled: true - class: "Flextype\\Entries\\Expressions\\CollectionExpression" - csrf: - enabled: true - class: "Flextype\\Entries\\Expressions\\CsrfExpression" - var: - enabled: true - class: "Flextype\\Entries\\Expressions\\VarExpression" - field: - enabled: true - class: "Flextype\\Entries\\Expressions\\FieldExpression" - const: - enabled: true - class: "Flextype\\Entries\\Expressions\\ConstExpression" - url: - enabled: true - class: "Flextype\\Entries\\Expressions\\UrlExpression" directives: expressions: enabled: true enabled_globally: true path: "src/flextype/core/Entries/Directives/ExpressionsDirective.php" - opening_tag: "[[" - closing_tag: "]]" shortcodes: enabled: true enabled_globally: true @@ -175,9 +102,6 @@ entries: path: "src/flextype/core/Entries/Directives/TypesDirective.php" macros: debug: false - vars: - enabled: true - path: "src/flextype/core/Entries/Macros/VarsMacros.php" php: enabled: false path: "src/flextype/core/Entries/Macros/PhpMacros.php" @@ -722,6 +646,85 @@ parsers: type: enabled: true path: "src/flextype/core/Parsers/Shortcodes/TypeShortcode.php" + expressions: + cache: + enabled: true + string: '' + opening_variable_tag: "[[" + closing_variable_tag: "]]" + opening_block_tag: "[%" + closing_block_tag: "%]" + opening_comment_tag: "[#" + closing_comment_tag: "#]" + expressions: + math: + enabled: true + class: "Flextype\\Parsers\\Expressions\\MathExpression" + date: + enabled: true + class: "Flextype\\Parsers\\Expressions\\DateExpression" + actions: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ActionsExpression" + registry: + enabled: true + class: "Flextype\\Parsers\\Expressions\\RegistryExpression" + entries: + enabled: true + class: "Flextype\\Parsers\\Expressions\\EntriesExpression" + fetch: + enabled: true + has: + enabled: true + registry: + enabled: true + create: + enabled: false + move: + enabled: false + update: + enabled: false + delete: + enabled: false + fetch: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FetchExpression" + filesystem: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FilesystemExpression" + i18n: + enabled: true + class: "Flextype\\Parsers\\Expressions\\I18nExpression" + serializers: + enabled: true + class: "Flextype\\Parsers\\Expressions\\SerializersExpression" + parsers: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ParsersExpression" + slugify: + enabled: true + class: "Flextype\\Parsers\\Expressions\\SlugifyExpression" + strings: + enabled: true + class: "Flextype\\Parsers\\Expressions\\StringsExpression" + collection: + enabled: true + class: "Flextype\\Parsers\\Expressions\\CollectionExpression" + csrf: + enabled: true + class: "Flextype\\Parsers\\Expressions\\CsrfExpression" + var: + enabled: true + class: "Flextype\\Parsers\\Expressions\\VarExpression" + field: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FieldExpression" + const: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ConstExpression" + url: + enabled: true + class: "Flextype\\Parsers\\Expressions\\UrlExpression" # CORS # diff --git a/tests/fixtures/settings/settings.yaml b/tests/fixtures/settings/settings.yaml index 73c41028..5f911b6a 100644 --- a/tests/fixtures/settings/settings.yaml +++ b/tests/fixtures/settings/settings.yaml @@ -18,13 +18,13 @@ locale: en_US # Application Base url # -# Define application base url +# Define application base url (without trailing slash) base_url: '' # Application Base Path # -# Define application base path if application located in subdirectory -base_path: '/' +# Define application base path (without trailing and without starting slash) if application located in subdirectory +base_path: '' # Valid date format # @@ -76,81 +76,11 @@ entries: directory: 'entries' cache: string: "" - vars: - debug: false - expressions: - math: - enabled: true - class: "Flextype\\Entries\\Expressions\\MathExpression" - date: - enabled: true - class: "Flextype\\Entries\\Expressions\\DateExpression" - actions: - enabled: true - class: "Flextype\\Entries\\Expressions\\ActionsExpression" - registry: - enabled: true - class: "Flextype\\Entries\\Expressions\\RegistryExpression" - entries: - enabled: true - class: "Flextype\\Entries\\Expressions\\EntriesExpression" - fetch: - enabled: true - has: - enabled: true - registry: - enabled: true - create: - enabled: false - move: - enabled: false - update: - enabled: false - delete: - enabled: false - fetch: - enabled: true - class: "Flextype\\Entries\\Expressions\\FetchExpression" - filesystem: - enabled: true - class: "Flextype\\Entries\\Expressions\\FilesystemExpression" - i18n: - enabled: true - class: "Flextype\\Entries\\Expressions\\I18nExpression" - serializers: - enabled: true - class: "Flextype\\Entries\\Expressions\\SerializersExpression" - parsers: - enabled: true - class: "Flextype\\Entries\\Expressions\\ParsersExpression" - slugify: - enabled: true - class: "Flextype\\Entries\\Expressions\\SlugifyExpression" - strings: - enabled: true - class: "Flextype\\Entries\\Expressions\\StringsExpression" - collection: - enabled: true - class: "Flextype\\Entries\\Expressions\\CollectionExpression" - csrf: - enabled: true - class: "Flextype\\Entries\\Expressions\\CsrfExpression" - var: - enabled: true - class: "Flextype\\Entries\\Expressions\\VarExpression" - field: - enabled: true - class: "Flextype\\Entries\\Expressions\\FieldExpression" - const: - enabled: true - class: "Flextype\\Entries\\Expressions\\ConstExpression" directives: expressions: enabled: true enabled_globally: true path: "src/flextype/core/Entries/Directives/ExpressionsDirective.php" - opening_tag: "[[" - closing_tag: "]]" shortcodes: enabled: true enabled_globally: true @@ -172,9 +102,6 @@ entries: path: "src/flextype/core/Entries/Directives/TypesDirective.php" macros: debug: false - vars: - enabled: true - path: "src/flextype/core/Entries/Macros/VarsMacros.php" php: enabled: true path: "src/flextype/core/Entries/Macros/PhpMacros.php" @@ -333,17 +260,6 @@ cache: ssl_enabled: false ssl_verify: false default_ttl: 900 - cookie: - aware_of_untrustable_data: false - limited_memory_by_object: 4096 - default_ttl: 900 - couchbase: - host: '127.0.0.1' - port: 8091 - username: '' - password: '' - bucket_name: default - default_ttl: 900 couchdb: database: 'flextype' path: '/' @@ -356,25 +272,20 @@ cache: timeout: 10 default_ttl: 900 devnull: {} - devfalse: {} - devtrue: {} phparray: path: '/data' security_key: 'auto' - htaccess: true secure_file_manipulation: false default_ttl: 900 files: path: '/data' security_key: 'auto' - htaccess: true secure_file_manipulation: false cache_file_extension: txt default_ttl: 900 leveldb: path: '/data' security_key: 'auto' - htaccess: true default_ttl: 900 memcache: host: '127.0.0.1' @@ -421,15 +332,9 @@ cache: database: 0 opt_prefix: '' default_ttl: 900 - riak: - host: '127.0.0.1' - port: 8098 - prefix: 'riak' - default_ttl: 900 sqlite: path: '/data' security_key: auto - htaccess: true default_ttl: 900 ssdb: host: 127.0.0.1 @@ -674,12 +579,12 @@ parsers: path: "src/flextype/core/Parsers/Shortcodes/EntriesShortcode.php" fetch: enabled: true - php: - enabled: true - path: "src/flextype/core/Parsers/Shortcodes/PhpShortcode.php" date: enabled: true path: "src/flextype/core/Parsers/Shortcodes/DateShortcode.php" + php: + enabled: true + path: "src/flextype/core/Parsers/Shortcodes/PhpShortcode.php" raw: enabled: true path: "src/flextype/core/Parsers/Shortcodes/RawShortcode.php" @@ -741,6 +646,85 @@ parsers: type: enabled: true path: "src/flextype/core/Parsers/Shortcodes/TypeShortcode.php" + expressions: + cache: + enabled: true + string: '' + opening_variable_tag: "[[" + closing_variable_tag: "]]" + opening_block_tag: "[%" + closing_block_tag: "%]" + opening_comment_tag: "[#" + closing_comment_tag: "#]" + expressions: + math: + enabled: true + class: "Flextype\\Parsers\\Expressions\\MathExpression" + date: + enabled: true + class: "Flextype\\Parsers\\Expressions\\DateExpression" + actions: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ActionsExpression" + registry: + enabled: true + class: "Flextype\\Parsers\\Expressions\\RegistryExpression" + entries: + enabled: true + class: "Flextype\\Parsers\\Expressions\\EntriesExpression" + fetch: + enabled: true + has: + enabled: true + registry: + enabled: true + create: + enabled: false + move: + enabled: false + update: + enabled: false + delete: + enabled: false + fetch: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FetchExpression" + filesystem: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FilesystemExpression" + i18n: + enabled: true + class: "Flextype\\Parsers\\Expressions\\I18nExpression" + serializers: + enabled: true + class: "Flextype\\Parsers\\Expressions\\SerializersExpression" + parsers: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ParsersExpression" + slugify: + enabled: true + class: "Flextype\\Parsers\\Expressions\\SlugifyExpression" + strings: + enabled: true + class: "Flextype\\Parsers\\Expressions\\StringsExpression" + collection: + enabled: true + class: "Flextype\\Parsers\\Expressions\\CollectionExpression" + csrf: + enabled: true + class: "Flextype\\Parsers\\Expressions\\CsrfExpression" + field: + enabled: true + class: "Flextype\\Parsers\\Expressions\\FieldExpression" + var: + enabled: true + class: "Flextype\\Parsers\\Expressions\\VarExpression" + const: + enabled: true + class: "Flextype\\Parsers\\Expressions\\ConstExpression" + url: + enabled: true + class: "Flextype\\Parsers\\Expressions\\UrlExpression" # CORS # diff --git a/tests/src/flextype/core/Entries/Expressions/FieldExpressionTest.php b/tests/src/flextype/core/Entries/Expressions/FieldExpressionTest.php deleted file mode 100644 index cafef279..00000000 --- a/tests/src/flextype/core/Entries/Expressions/FieldExpressionTest.php +++ /dev/null @@ -1,18 +0,0 @@ -directory(FLEXTYPE_PATH_PROJECT . '/entries')->create(); -}); - -afterEach(function (): void { - filesystem()->directory(FLEXTYPE_PATH_PROJECT . '/entries')->delete(); -}); - -test('field expression', function () { - entries()->create('field', ['test' => '[[ field("id") ]]']); - expect(entries()->fetch('field')['test'])->toBe('field'); -}); \ No newline at end of file diff --git a/tests/src/flextype/core/Entries/Expressions/VarExpressionTest.php b/tests/src/flextype/core/Entries/Expressions/VarExpressionTest.php deleted file mode 100644 index 209c6adc..00000000 --- a/tests/src/flextype/core/Entries/Expressions/VarExpressionTest.php +++ /dev/null @@ -1,37 +0,0 @@ -directory(FLEXTYPE_PATH_PROJECT . '/entries')->create(); -}); - -afterEach(function (): void { - filesystem()->directory(FLEXTYPE_PATH_PROJECT . '/entries')->delete(); -}); - -test('var + field expression', function () { - entries()->create('var', [ - 'title' => 'Foo', - 'vars' => [ - 'foo' => 'Foo' - ], - 'test1' => '[[ var("foo") ]]', - 'test2' => "[[ get('vars.foo', 'Foo') ]]", - 'test3' => '[[ vars.foo ]]', - 'test4' => '[[ set("bar", "Bar") ]][[ bar ]]', - 'test5' => '[[ unset("bar") ]]', - 'test6' => '[[ delete("bar") ]]', - 'test7' => '[[ title ]] [[ get("title") ]]', - ]); - - expect(entries()->fetch('var')['test1'])->toBe('Foo'); - expect(entries()->fetch('var')['test2'])->toBe('Foo'); - expect(entries()->fetch('var')['test3'])->toBe('Foo'); - expect(entries()->fetch('var')['test4'])->toBe('Bar'); - expect(entries()->fetch('var')['test5'])->toBe(''); - expect(entries()->fetch('var')['test6'])->toBe(''); - expect(entries()->fetch('var')['test7'])->toBe('Foo Foo'); -}); \ No newline at end of file diff --git a/tests/src/flextype/core/Entries/Expressions/ActionsExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/ActionsExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/ActionsExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/ActionsExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/CollectionExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/CollectionExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/CollectionExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/CollectionExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/ConstExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/ConstExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/ConstExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/ConstExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/CsrfExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/CsrfExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/CsrfExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/CsrfExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/DateExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/DateExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/DateExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/DateExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/EntriesExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/EntriesExpressionTest.php similarity index 69% rename from tests/src/flextype/core/Entries/Expressions/EntriesExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/EntriesExpressionTest.php index 0df8ffd0..665ae017 100644 --- a/tests/src/flextype/core/Entries/Expressions/EntriesExpressionTest.php +++ b/tests/src/flextype/core/Parsers/Expressions/EntriesExpressionTest.php @@ -33,9 +33,9 @@ test('entries expression', function () { 'test' => '(type:bool) [[ entries().delete("foo") ]]', ]); - registry()->set('flextype.settings.entries.expressions.entries.delete.enabled', true); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.delete.enabled', true); expect(entries()->fetch('delete')['test'])->toBeTrue(); - registry()->set('flextype.settings.entries.expressions.entries.delete.enabled', false); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.delete.enabled', false); // copy entries()->create('copy-foo'); @@ -43,9 +43,9 @@ test('entries expression', function () { 'test' => '(type:bool) [[ entries().copy("copy-foo", "copy-foo-2") ]]', ]); - registry()->set('flextype.settings.entries.expressions.entries.copy.enabled', true); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.copy.enabled', true); expect(entries()->fetch('copy')['test'])->toBeTrue(); - registry()->set('flextype.settings.entries.expressions.entries.copy.enabled', false); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.copy.enabled', false); // move entries()->create('move-foo'); @@ -53,9 +53,9 @@ test('entries expression', function () { 'test' => '(type:bool) [[ entries().move("move-foo", "move-foo-2") ]]', ]); - registry()->set('flextype.settings.entries.expressions.entries.move.enabled', true); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.move.enabled', true); expect(entries()->fetch('move')['test'])->toBeTrue(); - registry()->set('flextype.settings.entries.expressions.entries.move.enabled', false); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.move.enabled', false); // update entries()->create('update-foo'); @@ -63,8 +63,8 @@ test('entries expression', function () { 'test' => '(type:bool) [[ entries().update("update-foo", {"title": "Foo"}) ]]', ]); - registry()->set('flextype.settings.entries.expressions.entries.update.enabled', true); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.update.enabled', true); expect(entries()->fetch('update')['test'])->toBeTrue(); expect(entries()->fetch('update-foo')['title'])->toBe('Foo'); - registry()->set('flextype.settings.entries.expressions.entries.update.enabled', false); + registry()->set('flextype.settings.parsers.expressions.expressions.entries.update.enabled', false); }); \ No newline at end of file diff --git a/tests/src/flextype/core/Entries/Expressions/FetchExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/FetchExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/FetchExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/FetchExpressionTest.php diff --git a/tests/src/flextype/core/Parsers/Expressions/FieldExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/FieldExpressionTest.php new file mode 100644 index 00000000..7fd676b0 --- /dev/null +++ b/tests/src/flextype/core/Parsers/Expressions/FieldExpressionTest.php @@ -0,0 +1,49 @@ +directory(FLEXTYPE_PATH_PROJECT . '/entries')->create(); +}); + +afterEach(function (): void { + filesystem()->directory(FLEXTYPE_PATH_PROJECT . '/entries')->delete(); +}); + +test('field expression', function () { + + entries()->create('field', [ + 'title' => 'Title', + '_' => [ + 'foo' => 'Foo', + 'level2' => [ + 'value' => 'Bar', + ] + ], + + // Get + 'test-get-1' => '[[ field("title") ]]', + 'test-get-2' => '[[ title ]]', + 'test-get-3' => '[[ _.foo ]]', + 'test-get-4' => "[[ fields().get('_.foo') ]]", + 'test-get-5' => "[[ fields().get('_.bar', 'Default') ]]", + + // Set add Get + 'test-set-1' => '[% fields().set("qwerty", "Qwerty") %]', + 'test-set-2' => '[[ fields().get("qwerty") ]] [[qwerty ]] [[ qwerty]] [[qwerty]] [[ qwerty ]]', + + // Delete + 'test-delete-1' => '[% fields().delete("qwerty") %]', + ]); + + expect(entries()->fetch('field')['test-get-1'])->toBe('Title'); + expect(entries()->fetch('field')['test-get-2'])->toBe('Title'); + expect(entries()->fetch('field')['test-get-3'])->toBe('Foo'); + expect(entries()->fetch('field')['test-get-4'])->toBe('Foo'); + expect(entries()->fetch('field')['test-get-5'])->toBe('Default'); + expect(entries()->fetch('field')['test-set-1'])->toBe(''); + expect(entries()->fetch('field')['test-set-2'])->toBe('Qwerty Qwerty Qwerty Qwerty Qwerty'); + expect(entries()->fetch('field')['test-delete-1'])->toBe(''); +}); \ No newline at end of file diff --git a/tests/src/flextype/core/Entries/Expressions/FilesystemExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/FilesystemExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/FilesystemExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/FilesystemExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/I18nExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/I18nExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/I18nExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/I18nExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/MathExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/MathExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/MathExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/MathExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/ParsersExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/ParsersExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/ParsersExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/ParsersExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/RegistryExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/RegistryExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/RegistryExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/RegistryExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/SerializersExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/SerializersExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/SerializersExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/SerializersExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/SlugifyExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/SlugifyExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/SlugifyExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/SlugifyExpressionTest.php diff --git a/tests/src/flextype/core/Entries/Expressions/StringsExpressionTest.php b/tests/src/flextype/core/Parsers/Expressions/StringsExpressionTest.php similarity index 100% rename from tests/src/flextype/core/Entries/Expressions/StringsExpressionTest.php rename to tests/src/flextype/core/Parsers/Expressions/StringsExpressionTest.php diff --git a/tests/src/flextype/core/Parsers/Shortcodes/EntriesShortcodeTest.php b/tests/src/flextype/core/Parsers/Shortcodes/EntriesShortcodeTest.php index 555e6698..5a8dcdc8 100644 --- a/tests/src/flextype/core/Parsers/Shortcodes/EntriesShortcodeTest.php +++ b/tests/src/flextype/core/Parsers/Shortcodes/EntriesShortcodeTest.php @@ -31,9 +31,10 @@ test('entries shortcode', function () { $this->assertTrue(entries()->create('blog-3', ['title' => 'Blog', 'category-cat' => "(entries fetch id:'categories/cat' field:'title2' default:'Foo' /)"])); expect(entries()->fetch('blog-3')['category-cat'])->toBe('Foo'); - $this->assertTrue(entries()->create('shop', ['vars' => ['id' => 'shop', 'options' => 'collection=true'], 'title' => 'Shop', 'products' => "@type[array] (entries fetch id:'(var:id)' options:'(var:options)' /)"])); + $this->assertTrue(entries()->create('shop', ['_vars' => ['id' => 'shop', 'options' => 'collection=true'], 'title' => 'Shop', 'products' => "@type[array] (entries fetch id:'(field:_vars.id)' options:'(field:_vars.options)' /)"])); $this->assertTrue(entries()->create('shop/product-1', ['title' => 'Product 1'])); - expect(count(entries()->fetch('shop')['products']))->toBe(1); + $this->assertTrue(entries()->create('shop/product-2', ['title' => 'Product 2'])); + expect(count(entries()->fetch('shop')['products']))->toBe(2); }); test('entries shortcode disabled', function () { diff --git a/tests/src/flextype/core/Parsers/Shortcodes/FieldShortcodeTest.php b/tests/src/flextype/core/Parsers/Shortcodes/FieldShortcodeTest.php index ec284c19..1f1396dc 100644 --- a/tests/src/flextype/core/Parsers/Shortcodes/FieldShortcodeTest.php +++ b/tests/src/flextype/core/Parsers/Shortcodes/FieldShortcodeTest.php @@ -15,8 +15,40 @@ afterEach(function () { }); test('field shortcode', function () { - expect(entries()->create('foo', ['title' => '(field:id)']))->toBeTrue(); - expect(entries()->fetch('foo')['title'])->toBe('foo'); + expect(entries()->create('foo', [ + 'title' => 'Title', + + // get + 'test-get-1' => '(field:title)', + 'test-get-2' => '(field get:title)', + 'test-get-3' => '(field get:foo default:Foo)', + 'test-get-4' => '(field get:foo)Foo(/field)', + + // set + 'test-set-1' => '(field set:bar1 value:Bar1)(field:bar1)', + 'test-set-2' => '(field set:bar2)Bar2(/field)(field:bar2)', + 'test-set-3' => '(field set:level1.level2.level3)Multilevel(/field)(field:level1.level2.level3)', + 'test-set-4' => '(field set:_.foo)Foo(/field)(field:_.foo)', + + // unset + 'test-unset-1' => '(field unset:bar1)(field:bar1)', + + // delete + 'test-delete-1' => '(field delete:bar1)(field:bar1)', + ]))->toBeTrue(); + + $foo = entries()->fetch('foo'); + + expect($foo['test-get-1'])->toBe('Title'); + expect($foo['test-get-2'])->toBe('Title'); + expect($foo['test-get-3'])->toBe('Foo'); + expect($foo['test-get-4'])->toBe('Foo'); + expect($foo['test-set-1'])->toBe('Bar1'); + expect($foo['test-set-2'])->toBe('Bar2'); + expect($foo['test-set-3'])->toBe('Multilevel'); + expect($foo['test-set-4'])->toBe('Foo'); + expect($foo['test-unset-1'])->toBe(''); + expect($foo['test-delete-1'])->toBe(''); }); test('field shortcode disabled', function () {