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:
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