openrat-cms

# OpenRat Content Management System
git clone http://git.code.weiherhei.de/openrat-cms.git
Log | Files | Refs

commit cd0191350e01210f7abd4d945f979c824fd555f6
parent 50bfff66d197f2fb7d0e408ce4159d3d016e081f
Author: Jan Dankert <develop@jandankert.de>
Date:   Wed, 13 May 2020 23:29:44 +0200

Refactoring: New Variable Resolver with support for namespaces, default values and nested value expressions.

Diffstat:
modules/cms/macros/MacroRunner.class.php | 48++++++++++++++++++++++++------------------------
modules/cms/model/BaseObject.class.php | 9++++++---
modules/configuration/ConfigurationLoader.class.php | 25++++++++++++++-----------
modules/util/VariableResolver.class.php | 132-------------------------------------------------------------------------------
modules/util/text/variables/README.md | 0
modules/util/text/variables/Value.class.php | 10++++++++++
modules/util/text/variables/ValueExpression.class.php | 24++++++++++++++++++++++++
modules/util/text/variables/VariableResolver.class.php | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/text/variables/VariablesTest.class.php | 18++++++++++++++++++
9 files changed, 302 insertions(+), 170 deletions(-)

diff --git a/modules/cms/macros/MacroRunner.class.php b/modules/cms/macros/MacroRunner.class.php @@ -9,7 +9,7 @@ use cms\model\Value; use logger\Logger; use util\ArrayUtils; use util\exception\OpenRatException; -use util\VariableResolver; +use util\text\variables\VariableResolver; class MacroRunner { @@ -35,29 +35,29 @@ class MacroRunner $resolver = new VariableResolver(); - $parameters = $resolver->resolveVariablesInArrayWith($parameter, [ - - 'setting' => function ($var) { - return ArrayUtils::getSubValue($this->page->getSettings(), explode('.', $var)); - }, - - 'element' => function ($var) { - $template = new Template($this->page->templateid); - $elements = $template->getElementNames(); - $elementid = array_search($var, $elements); - - $value = new Value(); - $value->publisher = $this->page->publisher; - $value->elementid = $elementid; - $value->element = new Element($elementid); - $value->element->load(); - $value->pageid = $this->page->pageid; - $value->languageid = $this->page->languageid; - $value->load(); - - return $value->getRawValue(); - } - ]); + $resolver->namespaceSeparator = ':'; + $resolver->defaultSeparator = '?'; + $resolver->addResolver('setting',function ($var) { + return ArrayUtils::getSubValue($this->page->getSettings(), explode('.', $var)); + }); + $resolver->addResolver('element',function ($var) { + $template = new Template($this->page->templateid); + $elements = $template->getElementNames(); + $elementid = array_search($var, $elements); + + $value = new Value(); + $value->publisher = $this->page->publisher; + $value->elementid = $elementid; + $value->element = new Element($elementid); + $value->element->load(); + $value->pageid = $this->page->pageid; + $value->languageid = $this->page->languageid; + $value->load(); + + return $value->getRawValue(); + }); + + $parameters = $resolver->resolveVariablesInArray($parameter); foreach ($parameters as $param_name => $param_value) { diff --git a/modules/cms/model/BaseObject.class.php b/modules/cms/model/BaseObject.class.php @@ -6,7 +6,7 @@ namespace cms\model; use util\ArrayUtils; use cms\publish\Publish; use phpseclib\Math\BigInteger; -use util\VariableResolver; +use util\text\variables\VariableResolver; use util\YAML; use template_engine\components\ElseComponent; @@ -1348,12 +1348,15 @@ SQL $settings = YAML::parse($this->settings); $resolver = new VariableResolver(); + $resolver->namespaceSeparator = ':'; // Resolve config variables. - $settings = $resolver->resolveVariablesInArray( $settings,'config', function ($var) { + $resolver->addResolver('config', function ($var) { global $conf; return ArrayUtils::getSubValue($conf,explode('.',$var) ); - }); + }); + + $settings = $resolver->resolveVariablesInArray( $settings ); return $settings; } diff --git a/modules/configuration/ConfigurationLoader.class.php b/modules/configuration/ConfigurationLoader.class.php @@ -2,7 +2,7 @@ namespace configuration; -use util\VariableResolver; +use util\text\variables\VariableResolver; use util\YAML; @@ -116,20 +116,23 @@ class ConfigurationLoader private function resolveVariables($config) { $resolver = new VariableResolver(); + $resolver->namespaceSeparator = ':'; + $resolver->defaultSeparator = '?'; - return $resolver->resolveVariablesInArrayWith($config, [ - - 'env' => function ($var) { + $resolver->addResolver('env',function ($var) { return getenv(strtoupper($var)); - }, - // http:... is a shortcut for server:http-... - 'http' => function ($var) { + }); + + // http:... is a shortcut for server:http-... + $resolver->addResolver('http', function ($var) { return @$_SERVER['HTTP_' . strtoupper($var)]; - }, - 'server' => function ($var) { + }); + + $resolver->addResolver('server',function ($var) { return @$_SERVER[strtoupper($var)]; - } - ]); + }); + + return $resolver->resolveVariablesInArray($config); } } diff --git a/modules/util/VariableResolver.class.php b/modules/util/VariableResolver.class.php @@ -1,131 +0,0 @@ -<?php - - -namespace util; - -/** - * VariableResolver for resolving variables in strings and arrays. - */ - -class VariableResolver -{ - public $begin = '${'; - public $end = '}'; - public $split = ':'; - - /** - * Resolving a variable in a text like 'name is ${env:username:default}.' - * - * @param $value array - * @param $key string - * @param $resolver callable - * @return array - */ - public function resolveVariablesInArray($value,$key,$resolver) { - - return $this->resolveVariablesInArrayWith( $value, [$key=>$resolver] ); - } - - - - /** - * Resolving a variable in a text like 'name is ${env:username:default}.' - * - * @param $value array - * @param $resolvers array List of resolvers - * @return array - */ - public function resolveVariablesInArrayWith($value,$resolvers) { - - // pass-by-reference - array_walk_recursive($value, function (&$item, $keyI) use ($resolvers) { - - $item = $this->resolveVariablesWith($item, $resolvers); - return $item; - }); - - return $value; - - } - - - - /** - * Resolving a variable in a text like 'name is ${env:username:default}'. - * - * @param $value - * @param $key - * @param $resolver - * @return string - */ - public function resolveVariables($value,$key,$resolver) - { - return $this->resolveVariablesWith( $value,[$key=>$resolver] ); - } - - - - /** - * Resolving a variable in a text like 'name is ${env:username:default}'. - * - * @param $value - * @param $key - * @param $resolver - * @return string - */ - public function resolveVariablesWith($value, $resolvers) - { - $offset = 0; - - while( true ) - { - if ( $offset >= strlen($value) ) - return $value; - - $pos = strpos($value, $this->begin, $offset); - - if ($pos === FALSE) - return $value; - - - $posEnd = strpos($value, $this->end, $offset); - - if ($posEnd === FALSE) - return $value; - - $names = explode( $this->split, substr($value, $pos + strlen($this->begin ), $posEnd - strlen($this->begin) - $pos )); - $resolverName = $names[0]; - $key = @$names[1]; - $default = @$names[2]; - - $varValue = $default; - - if ( isset( $resolvers[$resolverName] )) { - - $resolverFunction = $resolvers[ $resolverName ]; - - if ( is_callable($resolverFunction ) ) - { - $result = $resolverFunction($key); - - if ( $result ) - $varValue = $result; - } - } - - $value = substr($value, 0, $pos) . $varValue . substr($value,$posEnd + strlen($this->end)); - $offset = $pos + strlen($varValue); - } - - return $value; - } -} - -/** Usage: * - -$resolver = new VariableResolver(); -print_r( $resolver->resolveVariables('Born in the ${bruce:birthcountry}.','bruce',function($name){return 'USA';} ) ); -print_r( $resolver->resolveVariables('Born in ${bruce:birthyear:19??}.','bruce',function($name){return '';} ) ); -print_r( $resolver->resolveVariables('Born in the ${bruce:birthcountry}.${x:y}${x}','bruce',function($name){return 'USA';} ) ); -exit; -*/- \ No newline at end of file diff --git a/modules/util/text/variables/README.md b/modules/util/text/variables/README.md diff --git a/modules/util/text/variables/Value.class.php b/modules/util/text/variables/Value.class.php @@ -0,0 +1,9 @@ +<?php + +namespace util\text\variables; + +class Value +{ + public $expressions = []; + +}+ \ No newline at end of file diff --git a/modules/util/text/variables/ValueExpression.class.php b/modules/util/text/variables/ValueExpression.class.php @@ -0,0 +1,23 @@ +<?php + +namespace util\text\variables; + +class ValueExpression +{ + public $prefix; + public $name; + public $default; + + /** + * ValueExpression constructor. + * @param $prefix + * @param $name + * @param $default + */ + public function __construct($prefix, $name, $default) + { + $this->prefix = $prefix; + $this->name = $name; + $this->default = $default; + } +}+ \ No newline at end of file diff --git a/modules/util/text/variables/VariableResolver.class.php b/modules/util/text/variables/VariableResolver.class.php @@ -0,0 +1,205 @@ +<?php + + +namespace util\text\variables; + +/** + * VariableResolver for resolving variables in strings and arrays. + */ + +class VariableResolver +{ + public $marker = '$'; + public $open = '{'; + public $close = '}'; + public $namespaceSeparator = '.'; + public $defaultSeparator = ':'; + + private $resolvers = []; + + private $value = null; + + + + public function addDefaultResolver( $resolver ) { + $this->resolvers[''] = $resolver; + } + public function addResolver( $key, $resolver ) { + $this->resolvers[$key] = $resolver; + } + + + /** + * Resolving a variable in a text like 'name is ${env:username:default}.' + * + * @param $value array + * @param $key string + * @param $resolver callable + * @return array + */ + public function resolveVariables($value) { + + $this->parseString( $value ); + return $this->toString(); + } + + /** + * Resolving a variable in a text like 'name is ${env.username:default}.' + * + * @param $value array + * @return array + */ + public function resolveVariablesInArray($value) { + + // pass-by-reference + array_walk_recursive($value, function (&$item, $keyI) { + + $item = $this->resolveVariables($item); + return $item; + }); + + return $value; + + } + + /** + * Resolving a variable in a text like 'name is ${env:username:default}'. + * + * @param $value + * @param $key + * @param $resolver + * @return string + */ + public function parseString($value) + { + $this->value = $this->parseToValue($value); + } + + private function toString() + { + return $this->render( $this->value ); + } + + + + public function render( $value ) + { + $text = ''; + + foreach( $value->expressions as $expression ) + { + if ( $expression instanceof ValueExpression ) { + $key = $this->render($expression->prefix); + $resolver = @$this->resolvers[ $key ]; + $v = ''; + if ( is_callable($resolver) ) { + $v = $resolver( $this->render($expression->name) ); + } + if ( ! $v ) + $v = $this->render($expression->default); + + $text .= $v; + }else { + $text .= strval( $expression ); + } + } + + return $text; + } + + + private function findNextPosOfChar( $haystack, $needle ) { + + $chars = str_split($haystack); + $depth = 0; + $pos = 0; + foreach( $chars as $char) { + + if ( $char == $this->open ) + $depth++; + elseif ( $char == $needle && $depth == 0 ) + return $pos; + elseif ( $char == $this->close ) + $depth--; + + $pos++; + } + + return false; + + } + + /** + * @param $inputText + * @return Value + */ + public function parseToValue($inputText) + { + $v = new Value(); + + while (true) { + + if ( ! $inputText ) + break; + + // Search the next variable marker '$' + $nextVariableMarkerPos = strpos($inputText, $this->marker.$this->open); + + if ($nextVariableMarkerPos === false ) + { + // no variable found. + $v->expressions[] = $inputText; + break; + } + else + { + $v->expressions[] = substr($inputText,0,$nextVariableMarkerPos); + + $inputText = substr($inputText,$nextVariableMarkerPos+strlen($this->marker)+strlen($this->open)); + + $pos = $this->findNextPosOfChar($inputText,$this->close); + + if ( $pos === false) + throw new \RuntimeException('non-closed variable: '.$inputText); + + $vv = substr($inputText,0,$pos); + $namespace = ''; + $default = ''; + + $prefixSepPos = $this->findNextPosOfChar($vv,$this->namespaceSeparator); + if ( $prefixSepPos!==false) { + $namespace = substr($vv,0,$prefixSepPos); + $vv = substr($vv,$prefixSepPos+strlen($this->namespaceSeparator)); + } + + $defaultSepPos = $this->findNextPosOfChar($vv,$this->defaultSeparator); + if ( $defaultSepPos!==false) { + $default = substr($vv,$defaultSepPos+strlen($this->defaultSeparator)); + $vv = substr($vv, 0,$defaultSepPos); + } + + $v->expressions[] = new ValueExpression($this->parseToValue($namespace),$this->parseToValue($vv),$this->parseToValue($default)); + + $inputText = substr($inputText,$pos+1); + } + + } + + return $v; + } + + + public static function createExpression($type, $name) + { + return $type . '{' . $name . '}'; + } +} + +/** Usage: * + +$resolver = new VariableResolver(); +print_r( $resolver->resolveVariables('Born in the ${bruce:birthcountry}.','bruce',function($name){return 'USA';} ) ); +print_r( $resolver->resolveVariables('Born in ${bruce:birthyear:19??}.','bruce',function($name){return '';} ) ); +print_r( $resolver->resolveVariables('Born in the ${bruce:birthcountry}.${x:y}${x}','bruce',function($name){return 'USA';} ) ); +exit; +*/+ \ No newline at end of file diff --git a/modules/util/text/variables/VariablesTest.class.php b/modules/util/text/variables/VariablesTest.class.php @@ -0,0 +1,17 @@ +<?php + +require_once('../../../autoload.php'); + +header('Content-Type: text/plain'); +set_time_limit(2); + +$res = new \util\text\variables\VariableResolver(); + +$example = 'Hello ${planet:unknown planet}! Are you ok? My name is ${me.name:unnamed} and robots name is ${me.${nix.nada:name}}, i was born ${me.date:before some years}'; +#$example = 'Hello ${planet:unknown planet}! Are you ok? My name is ${me.name:unnamed}. I was born ${me.date:before some years}.'; +$res->addDefaultResolver( function($x) {return 'world';} ); +$res->addResolver('me', function($t) {if ($t == 'name') return 'alice';return '';}); + +echo 'result: '.$res->resolveVariables( $example ); +echo "\n\n"; +print_r($res);+ \ No newline at end of file