mirror of
				https://github.com/Mibew/handlebars.php.git
				synced 2025-11-04 12:05:09 +03:00 
			
		
		
		
	Merge branch 'Mibew-improved_arguments_parsing'
This commit is contained in:
		
						commit
						ec108c0bac
					
				
							
								
								
									
										189
									
								
								src/Handlebars/Arguments.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								src/Handlebars/Arguments.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,189 @@
 | 
			
		||||
<?php
 | 
			
		||||
/**
 | 
			
		||||
 * This file is part of Handlebars-php
 | 
			
		||||
 *
 | 
			
		||||
 * PHP version 5.3
 | 
			
		||||
 *
 | 
			
		||||
 * @category  Xamin
 | 
			
		||||
 * @package   Handlebars
 | 
			
		||||
 * @author    Dmitriy Simushev <simushevds@gmail.com>
 | 
			
		||||
 * @copyright 2014 Authors
 | 
			
		||||
 * @license   MIT <http://opensource.org/licenses/MIT>
 | 
			
		||||
 * @version   GIT: $Id$
 | 
			
		||||
 * @link      http://xamin.ir
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Handlebars;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encapsulates helpers arguments.
 | 
			
		||||
 *
 | 
			
		||||
 * @category  Xamin
 | 
			
		||||
 * @package   Handlebars
 | 
			
		||||
 * @author    Dmitriy Simushev <simushevds@gmail.com>
 | 
			
		||||
 * @copyright 2014 Authors
 | 
			
		||||
 * @license   MIT <http://opensource.org/licenses/MIT>
 | 
			
		||||
 * @version   Release: @package_version@
 | 
			
		||||
 * @link      http://xamin.ir
 | 
			
		||||
 */
 | 
			
		||||
class Arguments
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * List of named arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $namedArgs = array();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * List of positional arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @var array
 | 
			
		||||
     */
 | 
			
		||||
    protected $positionalArgs = array();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The original arguments string that was used to fill in arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $originalString = '';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Class constructor.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $args_string Arguments string as passed to a helper.
 | 
			
		||||
     */
 | 
			
		||||
    public function __construct($args_string = false)
 | 
			
		||||
    {
 | 
			
		||||
        $this->originalString = $args_string;
 | 
			
		||||
 | 
			
		||||
        if ($args_string) {
 | 
			
		||||
            $this->parse($args_string);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns string representation of the arguments list.
 | 
			
		||||
     *
 | 
			
		||||
     * This method is here mostly for backward compatibility reasons.
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    public function __toString()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->originalString;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list of named arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getNamedArguments()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->namedArgs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns a list of positional arguments.
 | 
			
		||||
     *
 | 
			
		||||
     * @return array
 | 
			
		||||
     */
 | 
			
		||||
    public function getPositionalArguments()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->positionalArgs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Breaks an argument string into arguments and stores them inside the
 | 
			
		||||
     * object.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $args_string Arguments string as passed to a helper.
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     * @throws \InvalidArgumentException
 | 
			
		||||
     */
 | 
			
		||||
    protected function parse($args_string)
 | 
			
		||||
    {
 | 
			
		||||
        $bad_chars = preg_quote(Context::NOT_VALID_NAME_CHARS, '#');
 | 
			
		||||
        $bad_seg_chars = preg_quote(Context::NOT_VALID_SEGMENT_NAME_CHARS, '#');
 | 
			
		||||
 | 
			
		||||
        $name_chunk = '(?:[^' . $bad_chars . '\s]+)|(?:\[[^' . $bad_seg_chars . ']+\])';
 | 
			
		||||
        $variable_name = '(?:\.\.\/)*(?:(?:' . $name_chunk . ')[\.\/])*(?:' . $name_chunk  . ')\.?';
 | 
			
		||||
        $special_variable_name = '@[a-z]+';
 | 
			
		||||
        $escaped_value = '(?:(?<!\\\\)".*?(?<!\\\\)"|(?<!\\\\)\'.*?(?<!\\\\)\')';
 | 
			
		||||
        $argument_name = $name_chunk;
 | 
			
		||||
        $argument_value = $variable_name . '|' . $escaped_value . '|' . $special_variable_name;
 | 
			
		||||
        $positional_argument = '#^(' . $argument_value . ')#';
 | 
			
		||||
        $named_argument = '#^(' . $argument_name . ')\s*=\s*(' . $argument_value . ')#';
 | 
			
		||||
 | 
			
		||||
        $current_str = trim($args_string);
 | 
			
		||||
 | 
			
		||||
        // Split arguments string
 | 
			
		||||
        while (strlen($current_str) !== 0) {
 | 
			
		||||
            if (preg_match($named_argument, $current_str, $matches)) {
 | 
			
		||||
                // Named argument found
 | 
			
		||||
                $name = $this->prepareArgumentName($matches[1]);
 | 
			
		||||
                $value = $this->prepareArgumentValue($matches[2]);
 | 
			
		||||
 | 
			
		||||
                $this->namedArgs[$name] = $value;
 | 
			
		||||
 | 
			
		||||
                // Remove found argument from arguments string.
 | 
			
		||||
                $current_str = ltrim(substr($current_str, strlen($matches[0])));
 | 
			
		||||
            } elseif (preg_match($positional_argument, $current_str, $matches)) {
 | 
			
		||||
                // A positional argument found. It cannot follow named arguments
 | 
			
		||||
                if (count($this->namedArgs) !== 0) {
 | 
			
		||||
                    throw new \InvalidArgumentException('Positional arguments cannot follow named arguments');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $this->positionalArgs[] = $this->prepareArgumentValue($matches[1]);
 | 
			
		||||
 | 
			
		||||
                // Remove found argument from arguments string.
 | 
			
		||||
                $current_str = ltrim(substr($current_str, strlen($matches[0])));
 | 
			
		||||
            } else {
 | 
			
		||||
                throw new \InvalidArgumentException('Malformed arguments string');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepares argument's value to add to arguments list.
 | 
			
		||||
     *
 | 
			
		||||
     * The method unescapes value and wrap it into \Handlebars\String class if
 | 
			
		||||
     * needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $value Argument's value
 | 
			
		||||
     *
 | 
			
		||||
     * @return string|\Handlebars\String
 | 
			
		||||
     */
 | 
			
		||||
    protected function prepareArgumentValue($value)
 | 
			
		||||
    {
 | 
			
		||||
        // Check if argument's value is a quoted string literal
 | 
			
		||||
        if ($value[0] == "'" || $value[0] == '"') {
 | 
			
		||||
            // Remove enclosing quotes and unescape
 | 
			
		||||
            $value = new String(stripcslashes(substr($value, 1, -1)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepares argument's name.
 | 
			
		||||
     *
 | 
			
		||||
     * Remove sections braces if needed.
 | 
			
		||||
     *
 | 
			
		||||
     * @param strign $name Argument's name
 | 
			
		||||
     *
 | 
			
		||||
     * @return string
 | 
			
		||||
     */
 | 
			
		||||
    protected function prepareArgumentName($name)
 | 
			
		||||
    {
 | 
			
		||||
        // Check if argument's name is a segment
 | 
			
		||||
        if ($name[0] == '[') {
 | 
			
		||||
            $name = substr($name, 1, -1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $name;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -590,38 +590,14 @@ class Template
 | 
			
		||||
     */
 | 
			
		||||
    public function parseNamedArguments($string)
 | 
			
		||||
    {
 | 
			
		||||
        $variableName = '(?:(?:[^\'"\[\]\s]|\[.+?\])+)';
 | 
			
		||||
        $escapedValue = '(?:(?<!\\\\)".*?(?<!\\\\)"|(?<!\\\\)\'.*?(?<!\\\\)\')';
 | 
			
		||||
 | 
			
		||||
        // Get list of named arguemnts
 | 
			
		||||
        $matches = array();
 | 
			
		||||
        preg_match_all(
 | 
			
		||||
            '#(' . $variableName . ')\s*=\s*(' . $escapedValue . '|' . $variableName . ')#',
 | 
			
		||||
            $string,
 | 
			
		||||
            $matches,
 | 
			
		||||
            PREG_SET_ORDER
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $args = array();
 | 
			
		||||
        for ($x = 0, $c = count($matches); $x < $c; $x++) {
 | 
			
		||||
            $name = $matches[$x][1];
 | 
			
		||||
            $value = $matches[$x][2];
 | 
			
		||||
 | 
			
		||||
            // Check if argument's name is a segment
 | 
			
		||||
            if ($name[0] == '[') {
 | 
			
		||||
                $name = substr($name, 1, -1);
 | 
			
		||||
        if ($string instanceof Arguments) {
 | 
			
		||||
            // This code is needed only for backward compatibility
 | 
			
		||||
            $args = $string;
 | 
			
		||||
        } else {
 | 
			
		||||
            $args = new Arguments($string);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            // Check if argument's value is a quoted string literal
 | 
			
		||||
            if ($value[0] == "'" || $value[0] == '"') {
 | 
			
		||||
                // Remove enclosing quotes and unescape
 | 
			
		||||
                $value = new \Handlebars\String(stripcslashes(substr($value, 1, -1)));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $args[$name] = $value;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $args;
 | 
			
		||||
        return $args->getNamedArguments();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -634,23 +610,13 @@ class Template
 | 
			
		||||
     */
 | 
			
		||||
    public function parseArguments($string)
 | 
			
		||||
    {
 | 
			
		||||
        $args = array();
 | 
			
		||||
        preg_match_all('#(?:[^\'"\[\]\s]|\[.+?\])+|(?<!\\\\)("|\')(?:[^\\\\]|\\\\.)*?\1|\S+#s', $string, $args);
 | 
			
		||||
        $args = isset($args[0]) ? $args[0] : array();
 | 
			
		||||
 | 
			
		||||
        for ($x = 0, $argc = count($args); $x < $argc; $x++) {
 | 
			
		||||
            // check to see if argument is a quoted string literal
 | 
			
		||||
            if ($args[$x][0] == "'" || $args[$x][0] == '"') {
 | 
			
		||||
                if ($args[$x][0] === substr($args[$x], -1)) {
 | 
			
		||||
                    // remove enclosing quotes and unescape
 | 
			
		||||
                    $args[$x] = new \Handlebars\String(stripcslashes(substr($args[$x], 1, strlen($args[$x]) - 2)));
 | 
			
		||||
        if ($string instanceof Arguments) {
 | 
			
		||||
            // This code is needed only for backward compatibility
 | 
			
		||||
            $args = $string;
 | 
			
		||||
        } else {
 | 
			
		||||
                    throw new \RuntimeException("Malformed string: " . $args);
 | 
			
		||||
                }
 | 
			
		||||
            $args = new Arguments($string);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $args;
 | 
			
		||||
        return $args->getPositionalArguments();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -858,6 +858,85 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
 | 
			
		||||
        $this->assertEquals($args, $expected_array);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test Combined Arguments Parser
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $arg_string argument text
 | 
			
		||||
     * @param        $positional_args
 | 
			
		||||
     * @param        $named_args
 | 
			
		||||
     *
 | 
			
		||||
     * @dataProvider combinedArgumentsParserProvider
 | 
			
		||||
     *
 | 
			
		||||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
    public function testCombinedArgumentsParser($arg_string, $positional_args, $named_args)
 | 
			
		||||
    {
 | 
			
		||||
        $args = new \Handlebars\Arguments($arg_string);
 | 
			
		||||
 | 
			
		||||
        // Get the string version of the arguments array
 | 
			
		||||
        $stringify = function ($a) {
 | 
			
		||||
            return (string)$a;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if ($positional_args !== false) {
 | 
			
		||||
            $this->assertEquals(
 | 
			
		||||
                array_map($stringify, $args->getPositionalArguments()),
 | 
			
		||||
                $positional_args
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        if ($named_args !== false) {
 | 
			
		||||
            $this->assertEquals(
 | 
			
		||||
                array_map($stringify, $args->getNamedArguments()),
 | 
			
		||||
                $named_args
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function combinedArgumentsParserProvider()
 | 
			
		||||
    {
 | 
			
		||||
        $result = array();
 | 
			
		||||
 | 
			
		||||
        // Use data provider for positional arguments parser
 | 
			
		||||
        foreach ($this->argumentParserProvider() as $dataSet) {
 | 
			
		||||
            $result[] = array(
 | 
			
		||||
                $dataSet[0],
 | 
			
		||||
                $dataSet[1],
 | 
			
		||||
                false,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Use data provider for named arguments parser
 | 
			
		||||
        foreach ($this->namedArgumentParserProvider() as $dataSet) {
 | 
			
		||||
            $result[] = array(
 | 
			
		||||
                $dataSet[0],
 | 
			
		||||
                false,
 | 
			
		||||
                $dataSet[1],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add test cases with combined arguments
 | 
			
		||||
        return array_merge(
 | 
			
		||||
            $result,
 | 
			
		||||
            array(
 | 
			
		||||
                array(
 | 
			
		||||
                    'arg1 arg2 arg3=value1 arg4="value2"',
 | 
			
		||||
                    array('arg1', 'arg2'),
 | 
			
		||||
                    array('arg3' => 'value1', 'arg4' => 'value2')
 | 
			
		||||
                ),
 | 
			
		||||
                array(
 | 
			
		||||
                    '@first arg=@last',
 | 
			
		||||
                    array('@first'),
 | 
			
		||||
                    array('arg' => '@last')
 | 
			
		||||
                ),
 | 
			
		||||
                array(
 | 
			
		||||
                    '[seg arg1] [seg arg2] = [seg value "1"]',
 | 
			
		||||
                    array('[seg arg1]'),
 | 
			
		||||
                    array('seg arg2' => '[seg value "1"]')
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function stringLiteralInCustomHelperProvider()
 | 
			
		||||
    {
 | 
			
		||||
        return array(
 | 
			
		||||
@ -983,4 +1062,14 @@ class HandlebarsTest extends \PHPUnit_Framework_TestCase
 | 
			
		||||
        $this->assertEquals('good', $engine->render('{{#with b}}{{#if this}}{{../../a}}{{/if}}{{/with}}', array('a' => 'good', 'b' => 'stump')));
 | 
			
		||||
        $this->assertEquals('good', $engine->render('{{#with b}}{{#unless false}}{{../../a}}{{/unless}}{{/with}}', array('a' => 'good', 'b' => 'stump')));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test of Arguments to string conversion.
 | 
			
		||||
     */
 | 
			
		||||
    public function testArgumentsString()
 | 
			
		||||
    {
 | 
			
		||||
        $argsString = 'foo bar [foo bar] baz="value"';
 | 
			
		||||
        $args = new \Handlebars\Arguments($argsString);
 | 
			
		||||
        $this->assertEquals($argsString, (string)$args);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user