From b4c019372b923c0d2367d9ad6b8a3aaaf1c1c136 Mon Sep 17 00:00:00 2001 From: Dmitriy Simushev Date: Tue, 4 Feb 2014 16:11:29 +0000 Subject: [PATCH] Add segment-literal notation for expressions --- src/Handlebars/Context.php | 52 ++++++++++++++++++++++++++++++++-- src/Handlebars/Template.php | 2 +- tests/Xamin/HandlebarsTest.php | 12 ++++++-- 3 files changed, 61 insertions(+), 5 deletions(-) mode change 100755 => 100644 src/Handlebars/Context.php diff --git a/src/Handlebars/Context.php b/src/Handlebars/Context.php old mode 100755 new mode 100644 index 946a5be..d9c2b73 --- a/src/Handlebars/Context.php +++ b/src/Handlebars/Context.php @@ -11,6 +11,7 @@ * @author Behrooz Shabani * @author Chris Gray * @author Ulrik Lystbaek + * @author Dmitriy Simushev * @copyright 2012 (c) ParsPooyesh Co * @copyright 2013 (c) Behrooz Shabani * @copyright 2013 (c) f0ruD A @@ -38,6 +39,17 @@ namespace Handlebars; class Context { + /** + * List of charcters that cannot be used in identifiers. + */ + const NOT_VALID_NAME_CHARS = '!"#%&\'()*+,./;<=>@[\\]^`{|}~'; + + /** + * List of characters that cannot be used in identifiers in segment-literal + * notation. + */ + const NOT_VALID_SEGMENT_NAME_CHARS = "]"; + /** * @var array stack for context only top stack is available */ @@ -179,13 +191,14 @@ class Context /** * Get a available from current context * Supported types : - * variable , ../variable , variable.variable , . + * variable , ../variable , variable.variable , variable.[variable] , . * * @param string $variableName variable name to get from current context * @param boolean $strict strict search? if not found then throw exception * * @throws \InvalidArgumentException in strict mode and variable not found * @throws \RuntimeException if supplied argument is a malformed quoted string + * @throws \InvalidArgumentException if variable name is invalid * @return mixed */ public function get($variableName, $strict = false) @@ -228,7 +241,7 @@ class Context } elseif ($variableName == '@key') { $current = $this->lastKey(); } else { - $chunks = explode('.', $variableName); + $chunks = $this->_splitVariableName($variableName); foreach ($chunks as $chunk) { if (is_string($current) and $current == '') { return $current; @@ -275,4 +288,39 @@ class Context return $value; } + /** + * Splits variable name to chunks. + * + * @param string $variable_name Fully qualified name of a variable. + * + * @throws \InvalidArgumentException if variable name is invalid. + * @return array + */ + private function _splitVariableName($variable_name) + { + $bad_chars = preg_quote(self::NOT_VALID_NAME_CHARS, '/'); + $bad_seg_chars = preg_quote(self::NOT_VALID_SEGMENT_NAME_CHARS, '/'); + + $name_pattern = "(?:[^" . $bad_chars . "\s]+)|(?:\[[^" . $bad_seg_chars . "]+\])"; + $check_pattern = "/^((" . $name_pattern . ")\.)*(" . $name_pattern . ")\.?$/"; + $get_pattern = "/(?:" . $name_pattern . ")/"; + + if (!preg_match($check_pattern, $variable_name)) { + throw new \InvalidArgumentException('variable name is invalid'); + } + + preg_match_all($get_pattern, $variable_name, $matches); + + $chunks = array(); + foreach ($matches[0] as $chunk) { + // Remove wrapper braces if needed + if ($chunk[0] == '[') { + $chunk = substr($chunk, 1, -1); + } + $chunks[] = $chunk; + } + + return $chunks; + } + } diff --git a/src/Handlebars/Template.php b/src/Handlebars/Template.php index e28b220..0240e9a 100644 --- a/src/Handlebars/Template.php +++ b/src/Handlebars/Template.php @@ -455,7 +455,7 @@ class Template public function parseArguments($string) { $args = array(); - preg_match_all('#(?value = 'value'; - $test->array = array('a' => '1', 'b' => '2'); + $test->array = array( + 'a' => '1', + 'b' => '2', + '!"#%&\'()*+,./;<=>@[\\^`{|}~ ' => '3', + ); $context = new \Handlebars\Context($test); $this->assertEquals('value', $context->get('value')); $this->assertEquals('value', $context->get('value', true)); + $this->assertEquals('value', $context->get('[value]', true)); $this->assertEquals('1', $context->get('array.a', true)); $this->assertEquals('2', $context->get('array.b', true)); + $this->assertEquals('3', $context->get('array.[!"#%&\'()*+,./;<=>@[\\^`{|}~ ]', true)); $new = array('value' => 'new value'); $context->push($new); $this->assertEquals('new value', $context->get('value')); @@ -546,7 +552,9 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase array('arg1 "arg\"2" "\"arg3\""', array("arg1",'arg"2', '"arg3"')), array("'arg1 arg2'", array("arg1 arg2")), array("arg1 arg2 'arg number 3'", array("arg1","arg2","arg number 3")), - array('arg1 "arg\"2" "\\\'arg3\\\'"', array("arg1",'arg"2', "'arg3'")) + array('arg1 "arg\"2" "\\\'arg3\\\'"', array("arg1",'arg"2', "'arg3'")), + array('arg1 arg2.[value\'s "segment"].val', array("arg1", 'arg2.[value\'s "segment"].val')), + array('"arg1.[value 1]" arg2', array("arg1.[value 1]", 'arg2')), ); } /**