Merge pull request #47 from Mibew/improved_ids

Add segment-literal notation for expressions
This commit is contained in:
Behrooz Shabani 2014-02-05 13:11:12 +03:30
commit 40f0eb9b7b
3 changed files with 61 additions and 5 deletions

52
src/Handlebars/Context.php Executable file → Normal file
View File

@ -11,6 +11,7 @@
* @author Behrooz Shabani <everplays@gmail.com> * @author Behrooz Shabani <everplays@gmail.com>
* @author Chris Gray <chris.w.gray@gmail.com> * @author Chris Gray <chris.w.gray@gmail.com>
* @author Ulrik Lystbaek <ulrik@bettertaste.dk> * @author Ulrik Lystbaek <ulrik@bettertaste.dk>
* @author Dmitriy Simushev <simushevds@gmail.com>
* @copyright 2012 (c) ParsPooyesh Co * @copyright 2012 (c) ParsPooyesh Co
* @copyright 2013 (c) Behrooz Shabani * @copyright 2013 (c) Behrooz Shabani
* @copyright 2013 (c) f0ruD A * @copyright 2013 (c) f0ruD A
@ -38,6 +39,17 @@ namespace Handlebars;
class Context 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 * @var array stack for context only top stack is available
*/ */
@ -179,13 +191,14 @@ class Context
/** /**
* Get a available from current context * Get a available from current context
* Supported types : * Supported types :
* variable , ../variable , variable.variable , . * variable , ../variable , variable.variable , variable.[variable] , .
* *
* @param string $variableName variable name to get from current context * @param string $variableName variable name to get from current context
* @param boolean $strict strict search? if not found then throw exception * @param boolean $strict strict search? if not found then throw exception
* *
* @throws \InvalidArgumentException in strict mode and variable not found * @throws \InvalidArgumentException in strict mode and variable not found
* @throws \RuntimeException if supplied argument is a malformed quoted string * @throws \RuntimeException if supplied argument is a malformed quoted string
* @throws \InvalidArgumentException if variable name is invalid
* @return mixed * @return mixed
*/ */
public function get($variableName, $strict = false) public function get($variableName, $strict = false)
@ -228,7 +241,7 @@ class Context
} elseif ($variableName == '@key') { } elseif ($variableName == '@key') {
$current = $this->lastKey(); $current = $this->lastKey();
} else { } else {
$chunks = explode('.', $variableName); $chunks = $this->_splitVariableName($variableName);
foreach ($chunks as $chunk) { foreach ($chunks as $chunk) {
if (is_string($current) and $current == '') { if (is_string($current) and $current == '') {
return $current; return $current;
@ -275,4 +288,39 @@ class Context
return $value; 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;
}
} }

View File

@ -455,7 +455,7 @@ class Template
public function parseArguments($string) public function parseArguments($string)
{ {
$args = array(); $args = array();
preg_match_all('#(?<!\\\\)("|\')(?:[^\\\\]|\\\\.)*?\1|\S+#s', $string, $args); preg_match_all('#(?:[^\'"\[\]\s]|\[.+?\])+|(?<!\\\\)("|\')(?:[^\\\\]|\\\\.)*?\1|\S+#s', $string, $args);
$args = isset($args[0])?$args[0]:array(); $args = isset($args[0])?$args[0]:array();
for ($x=0, $argc = count($args); $x<$argc;$x++) { for ($x=0, $argc = count($args); $x<$argc;$x++) {

View File

@ -476,12 +476,18 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
{ {
$test = new stdClass(); $test = new stdClass();
$test->value = 'value'; $test->value = 'value';
$test->array = array('a' => '1', 'b' => '2'); $test->array = array(
'a' => '1',
'b' => '2',
'!"#%&\'()*+,./;<=>@[\\^`{|}~ ' => '3',
);
$context = new \Handlebars\Context($test); $context = new \Handlebars\Context($test);
$this->assertEquals('value', $context->get('value')); $this->assertEquals('value', $context->get('value'));
$this->assertEquals('value', $context->get('value', true)); $this->assertEquals('value', $context->get('value', true));
$this->assertEquals('value', $context->get('[value]', true));
$this->assertEquals('1', $context->get('array.a', true)); $this->assertEquals('1', $context->get('array.a', true));
$this->assertEquals('2', $context->get('array.b', true)); $this->assertEquals('2', $context->get('array.b', true));
$this->assertEquals('3', $context->get('array.[!"#%&\'()*+,./;<=>@[\\^`{|}~ ]', true));
$new = array('value' => 'new value'); $new = array('value' => 'new value');
$context->push($new); $context->push($new);
$this->assertEquals('new value', $context->get('value')); $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 "arg\"2" "\"arg3\""', array("arg1",'arg"2', '"arg3"')),
array("'arg1 arg2'", array("arg1 arg2")), array("'arg1 arg2'", array("arg1 arg2")),
array("arg1 arg2 'arg number 3'", array("arg1","arg2","arg number 3")), 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')),
); );
} }
/** /**