mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	* php-cs-fixer: PSR2 rule. * php-cs-fixer: PSR2 rule - fix views. * Travis setup refactoring. * Add php-cs-fixer to travis cs tests. * Fix tests on hhvm-3.12 * improve travis config * composer update * revert composer update * improve travis config * Fix CS. * Extract config to separate classes. * Extract config to separate classes. * Add file header. * Force short array syntax. * binary_operator_spaces fixer * Fix broken tests * cast_spaces fixer * concat_space fixer * dir_constant fixer * ereg_to_preg fixer * function_typehint_space fixer * hash_to_slash_comment fixer * is_null fixer * linebreak_after_opening_tag fixer * lowercase_cast fixer * magic_constant_casing fixer * modernize_types_casting fixer * native_function_casing fixer * new_with_braces fixer * no_alias_functions fixer * no_blank_lines_after_class_opening fixer * no_blank_lines_after_phpdoc fixer * no_empty_comment fixer * no_empty_phpdoc fixer * no_empty_statement fixer * no_extra_consecutive_blank_lines fixer * no_leading_import_slash fixer * no_leading_namespace_whitespace fixer * no_mixed_echo_print fixer * no_multiline_whitespace_around_double_arrow fixer * no_multiline_whitespace_before_semicolons fixer * no_php4_constructor fixer * no_short_bool_cast fixer * no_singleline_whitespace_before_semicolons fixer * no_spaces_around_offset fixer * no_trailing_comma_in_list_call fixer * no_trailing_comma_in_singleline_array fixer * no_unneeded_control_parentheses fixer * no_unused_imports fixer * no_useless_return fixer * no_whitespace_before_comma_in_array fixer * no_whitespace_in_blank_line fixer * not_operator_with_successor_space fixer * object_operator_without_whitespace fixer * ordered_imports fixer * php_unit_construct fixer * php_unit_dedicate_assert fixer * php_unit_fqcn_annotation fixer * phpdoc_indent fixer * phpdoc_no_access fixer * phpdoc_no_empty_return fixer * phpdoc_no_package fixer * phpdoc_no_useless_inheritdoc fixer * Fix broken tests * phpdoc_return_self_reference fixer * phpdoc_single_line_var_spacing fixer * phpdoc_single_line_var_spacing fixer * phpdoc_to_comment fixer * phpdoc_trim fixer * phpdoc_var_without_name fixer * psr4 fixer * self_accessor fixer * short_scalar_cast fixer * single_blank_line_before_namespace fixer * single_quote fixer * standardize_not_equals fixer * ternary_operator_spaces fixer * trailing_comma_in_multiline_array fixer * trim_array_spaces fixer * protected_to_private fixer * unary_operator_spaces fixer * whitespace_after_comma_in_array fixer * `parent::setRules()` -> `$this->setRules()` * blank_line_after_opening_tag fixer * Update finder config. * Revert changes for YiiRequirementChecker. * Fix array formatting. * Add missing import. * Fix CS for new code merged from master. * Fix some indentation issues.
		
			
				
	
	
		
			1389 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1389 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						||
/**
 | 
						||
 * @link http://www.yiiframework.com/
 | 
						||
 * @copyright Copyright (c) 2008 Yii Software LLC
 | 
						||
 * @license http://www.yiiframework.com/license/
 | 
						||
 */
 | 
						||
 | 
						||
namespace yiiunit\framework\web;
 | 
						||
 | 
						||
use Yii;
 | 
						||
use yii\helpers\VarDumper;
 | 
						||
use yii\web\NotFoundHttpException;
 | 
						||
use yii\web\Request;
 | 
						||
use yii\web\UrlManager;
 | 
						||
use yii\web\UrlNormalizer;
 | 
						||
use yii\web\UrlNormalizerRedirectException;
 | 
						||
use yii\web\UrlRule;
 | 
						||
use yiiunit\TestCase;
 | 
						||
 | 
						||
/**
 | 
						||
 * @group web
 | 
						||
 */
 | 
						||
class UrlRuleTest extends TestCase
 | 
						||
{
 | 
						||
    protected function setUp()
 | 
						||
    {
 | 
						||
        parent::setUp();
 | 
						||
        $this->mockApplication();
 | 
						||
    }
 | 
						||
 | 
						||
    public function testCreateUrl()
 | 
						||
    {
 | 
						||
        $manager = new UrlManager(['cache' => null]);
 | 
						||
        $suites = $this->getTestsForCreateUrl();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                list($route, $params, $expected) = $test;
 | 
						||
                $url = $rule->createUrl($manager, $route, $params);
 | 
						||
                $this->assertSame($expected, $url, "Test#$i-$j: $name");
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    public function testParseRequest()
 | 
						||
    {
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => false,
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = $test[1];
 | 
						||
                $result = $rule->parseRequest($manager, $request);
 | 
						||
                if ($expected === false) {
 | 
						||
                    $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                } else {
 | 
						||
                    $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    public function testParseRequestWithNormalizer()
 | 
						||
    {
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => UrlNormalizer::className(),
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = isset($test[2]) ? $test[2] : $test[1];
 | 
						||
                try {
 | 
						||
                    $result = $rule->parseRequest($manager, $request);
 | 
						||
                    if ($expected === false) {
 | 
						||
                        $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                    } else {
 | 
						||
                        $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                    }
 | 
						||
                } catch (UrlNormalizerRedirectException $exc) {
 | 
						||
                    $this->assertEquals([$expected[0]] + $expected[1], $exc->url, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    public function testParseRequestWithUrlManagerCustomNormalizer()
 | 
						||
    {
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => UrlNormalizer::className(),
 | 
						||
                'action' => UrlNormalizer::ACTION_REDIRECT_PERMANENT,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = isset($test[2]) ? $test[2] : $test[1];
 | 
						||
                try {
 | 
						||
                    $result = $rule->parseRequest($manager, $request);
 | 
						||
                    if ($expected === false) {
 | 
						||
                        $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                    } else {
 | 
						||
                        $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                    }
 | 
						||
                } catch (UrlNormalizerRedirectException $exc) {
 | 
						||
                    $this->assertEquals(UrlNormalizer::ACTION_REDIRECT_PERMANENT, $exc->statusCode, "Test-statusCode#$i-$j: $name");
 | 
						||
                    $this->assertEquals([$expected[0]] + $expected[1], $exc->url, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => UrlNormalizer::className(),
 | 
						||
                'action' => UrlNormalizer::ACTION_REDIRECT_TEMPORARY,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = isset($test[2]) ? $test[2] : $test[1];
 | 
						||
                try {
 | 
						||
                    $result = $rule->parseRequest($manager, $request);
 | 
						||
                    if ($expected === false) {
 | 
						||
                        $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                    } else {
 | 
						||
                        $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                    }
 | 
						||
                } catch (UrlNormalizerRedirectException $exc) {
 | 
						||
                    $this->assertEquals(UrlNormalizer::ACTION_REDIRECT_TEMPORARY, $exc->statusCode, "Test-statusCode#$i-$j: $name");
 | 
						||
                    $this->assertEquals([$expected[0]] + $expected[1], $exc->url, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => UrlNormalizer::className(),
 | 
						||
                'action' => UrlNormalizer::ACTION_NOT_FOUND,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = $test[1];
 | 
						||
                try {
 | 
						||
                    $result = $rule->parseRequest($manager, $request);
 | 
						||
                    if ($expected === false) {
 | 
						||
                        $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                    } else {
 | 
						||
                        $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                    }
 | 
						||
                } catch (NotFoundHttpException $exc) {
 | 
						||
                    $this->assertFalse($expected, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => UrlNormalizer::className(),
 | 
						||
                'action' => null,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = isset($test[2]) ? $test[2] : $test[1];
 | 
						||
                $result = $rule->parseRequest($manager, $request);
 | 
						||
                if ($expected === false) {
 | 
						||
                    $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                } else {
 | 
						||
                    $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        $normalizerAction = function ($route) {
 | 
						||
            $route[1]['oldRoute'] = $route[0];
 | 
						||
            $route[0] = 'site/myCustomRoute';
 | 
						||
            return $route;
 | 
						||
        };
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => UrlNormalizer::className(),
 | 
						||
                'action' => $normalizerAction,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $request = new Request(['hostInfo' => 'http://en.example.com']);
 | 
						||
        $suites = $this->getTestsForParseRequest();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $tests) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            foreach ($tests as $j => $test) {
 | 
						||
                $request->pathInfo = $test[0];
 | 
						||
                $expected = isset($test[2]) ? $normalizerAction($test[2]) : $test[1];
 | 
						||
                $result = $rule->parseRequest($manager, $request);
 | 
						||
                if ($expected === false) {
 | 
						||
                    $this->assertFalse($result, "Test#$i-$j: $name");
 | 
						||
                } else {
 | 
						||
                    $this->assertEquals($expected, $result, "Test#$i-$j: $name");
 | 
						||
                }
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    public function testParseRequestWithUrlRuleCustomNormalizer()
 | 
						||
    {
 | 
						||
        $manager = new UrlManager([
 | 
						||
            'cache' => null,
 | 
						||
        ]);
 | 
						||
        $request = new Request([
 | 
						||
            'hostInfo' => 'http://en.example.com',
 | 
						||
            'pathInfo' => 'post/1-a/',
 | 
						||
        ]);
 | 
						||
 | 
						||
        $rule = new UrlRule([
 | 
						||
            'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
            'route' => 'post/index',
 | 
						||
            'normalizer' => false,
 | 
						||
        ]);
 | 
						||
        $result = $rule->parseRequest($manager, $request);
 | 
						||
        $this->assertFalse($result);
 | 
						||
 | 
						||
        $rule = new UrlRule([
 | 
						||
            'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
            'route' => 'post/index',
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => 'yii\web\UrlNormalizer',
 | 
						||
                'normalizeTrailingSlash' => false,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $result = $rule->parseRequest($manager, $request);
 | 
						||
        $this->assertFalse($result);
 | 
						||
 | 
						||
        $rule = new UrlRule([
 | 
						||
            'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
            'route' => 'post/index',
 | 
						||
            'normalizer' => [
 | 
						||
                'class' => 'yii\web\UrlNormalizer',
 | 
						||
                'normalizeTrailingSlash' => true,
 | 
						||
                'action' => null,
 | 
						||
            ],
 | 
						||
        ]);
 | 
						||
        $result = $rule->parseRequest($manager, $request);
 | 
						||
        $this->assertEquals(['post/index', ['page' => 1, 'tag' => 'a']], $result);
 | 
						||
    }
 | 
						||
 | 
						||
    public function testToString()
 | 
						||
    {
 | 
						||
        $suites = $this->getTestsForToString();
 | 
						||
        foreach ($suites as $i => $suite) {
 | 
						||
            list($name, $config, $test) = $suite;
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            $this->assertEquals($rule->__toString(), $test, "Test#$i: $name");
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    protected function getTestsForCreateUrl()
 | 
						||
    {
 | 
						||
        // structure of each test
 | 
						||
        //   message for the test
 | 
						||
        //   config for the URL rule
 | 
						||
        //   list of inputs and outputs
 | 
						||
        //     route
 | 
						||
        //     params
 | 
						||
        //     expected output
 | 
						||
        return [
 | 
						||
            [
 | 
						||
                'empty pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], ''],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], '?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'without param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], 'posts'],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], 'posts?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'parsing only',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'mode' => UrlRule::PARSING_ONLY,
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], false],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], 'post/1'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/1?tag=a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with param requirement',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 'abc'], false],
 | 
						||
                    ['post/index', ['page' => 1], 'post/1'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/1?tag=a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with multiple params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => '1abc'], false],
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/1-a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple params with special chars',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page-number:\d+>/<per_page:\d+>/<author.login>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], false],
 | 
						||
                    ['post/index', ['page-number' => '1', 'per_page' => '25'], false],
 | 
						||
                    ['post/index', ['page-number' => '1', 'per_page' => '25', 'author.login' => 'yiiuser'], 'post/1/25/yiiuser'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple params with leading non-letter chars',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<1page-number:\d+>/<-per_page:\d+>/<_author.login>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], false],
 | 
						||
                    ['post/index', ['1page-number' => '1', '-per_page' => '25'], false],
 | 
						||
                    ['post/index', ['1page-number' => '1', '-per_page' => '25', '_author.login' => 'yiiuser'], 'post/1/25/yiiuser'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with optional param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/a'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], 'post/2/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with optional param not in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple optional params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>/<sort:yes|no>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'sort' => 'yes'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'YES'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], 'post/a'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a', 'sort' => 'yes'], 'post/2/a'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a', 'sort' => 'no'], 'post/2/a/no'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'no'], 'post/a/no'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param and required param separated by dashes',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/-a'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], 'post/2-a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param at the end',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<tag>/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/a'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], 'post/a/2'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param at the beginning',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language>/<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['site/category', ['language' => 'en', 'category' => 'books'], 'books'],
 | 
						||
                    ['site/category', ['language' => 'pl', 'category' => 'books'], 'pl/books'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'two optional params at the beginning',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language>/<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['site/category', ['language' => 'en', 'category' => 'books'], ''],
 | 
						||
                    ['site/category', ['language' => 'en', 'category' => 'games'], 'games'],
 | 
						||
                    ['site/category', ['language' => 'pl', 'category' => 'games'], 'pl/games'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param at the beginning with suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => '<page>',
 | 
						||
                    'route' => 'page/view',
 | 
						||
                    'defaults' => ['page' => 'index'],
 | 
						||
                    'suffix' => '/',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['page/view', ['page' => 'index'], ''],
 | 
						||
                    ['page/view', ['page' => 'news'], 'news/'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'consecutive optional params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'tag' => 'a'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], 'post/2'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'b'], 'post/b'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'b'], 'post/2/b'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'consecutive optional params separated by dash',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'tag' => 'a'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], 'post/-'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'b'], 'post/-b'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a'], 'post/2-'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'b'], 'post/2-b'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional params - example from guide',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'tag' => ''],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => ''], 'posts'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => ''], 'posts/2'],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'news'], 'posts/2/news'],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'news'], 'posts/news'],
 | 
						||
                    // allow skip empty params on URL creation
 | 
						||
                    ['post/index', [], false],
 | 
						||
                    ['post/index', ['tag' => ''], false],
 | 
						||
                    ['post/index', ['page' => 1], 'posts'],
 | 
						||
                    ['post/index', ['page' => 2], 'posts/2'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'required params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'about-me',
 | 
						||
                    'route' => 'site/page',
 | 
						||
                    'defaults' => ['id' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['site/page', ['id' => 1], 'about-me'],
 | 
						||
                    ['site/page', ['id' => 2], false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'required default param',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'site/home',
 | 
						||
                    'defaults' => ['lang' => 'en'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['site/home', ['lang' => 'en'], ''],
 | 
						||
                    ['site/home', ['lang' => ''], false],
 | 
						||
                    ['site/home', [], false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'required default empty param',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'site/home',
 | 
						||
                    'defaults' => ['lang' => ''],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['site/home', ['lang' => ''], ''],
 | 
						||
                    ['site/home', ['lang' => 'en'], false],
 | 
						||
                    ['site/home', [], false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has parameters',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => [],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], 'post/index?page=1'],
 | 
						||
                    ['module/post/index', [], false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has parameters with regex',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller:post|comment>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => [],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1], 'post/index?page=1'],
 | 
						||
                    ['comment/index', ['page' => 1], 'comment/index?page=1'],
 | 
						||
                    ['test/index', ['page' => 1], false],
 | 
						||
                    ['post', [], false],
 | 
						||
                    ['module/post/index', [], false],
 | 
						||
                    ['post/index', ['controller' => 'comment'], 'post/index?controller=comment'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has default parameter',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller:post|comment>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => ['action' => 'index'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/view', ['page' => 1], 'post/view?page=1'],
 | 
						||
                    ['comment/view', ['page' => 1], 'comment/view?page=1'],
 | 
						||
                    ['test/view', ['page' => 1], false],
 | 
						||
                    ['test/index', ['page' => 1], false],
 | 
						||
                    ['post/index', ['page' => 1], 'post?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'empty pattern with suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '.html',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], ''],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], '?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'regular pattern with suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '.html',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], 'posts.html'],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], 'posts.html?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'empty pattern with slash suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '/',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], ''],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], '?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'regular pattern with slash suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '/',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', [], 'posts/'],
 | 
						||
                    ['comment/index', [], false],
 | 
						||
                    ['post/index', ['page' => 1], 'posts/?page=1'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                    'host' => 'http://<lang:en|fr>.example.com',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], 'http://en.example.com/post/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => 'http://<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], 'http://en.example.com/post/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with relative host info',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                    'host' => '//<lang:en|fr>.example.com',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], '//en.example.com/post/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with relative host info in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => '//<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a'], false],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'lang' => 'en'], '//en.example.com/post/a'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with unicode',
 | 
						||
                [
 | 
						||
                    'pattern' => '/blog/search/<tag:[a-zA-Zа-яА-Я0-9\_\+\-]{1,255}>',
 | 
						||
                    'route' => 'blog/search',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['blog/search', ['tag' => 'метра'], 'blog/search/%D0%BC%D0%B5%D1%82%D1%80%D0%B0'],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
        ];
 | 
						||
    }
 | 
						||
 | 
						||
    protected function getTestsForParseRequest()
 | 
						||
    {
 | 
						||
        // structure of each test
 | 
						||
        //   message for the test
 | 
						||
        //   config for the URL rule
 | 
						||
        //   list of inputs and outputs
 | 
						||
        //     pathInfo
 | 
						||
        //     expected result (in format [route, params]) with normalization disabled, or false if the rule doesn't apply
 | 
						||
        //     expected result if noralizer is enabled, or not set if result should be the same as without normalizer
 | 
						||
        return [
 | 
						||
            [
 | 
						||
                'empty pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['', ['post/index', []]],
 | 
						||
                    ['a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'without param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['posts', ['post/index', []]],
 | 
						||
                    ['a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with dot', // https://github.com/yiisoft/yii/issues/2945
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts.html',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['posts.html', ['post/index', []]],
 | 
						||
                    ['postsahtml', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'creation only',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'mode' => UrlRule::CREATION_ONLY,
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['posts', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1']]],
 | 
						||
                    ['post/a', ['post/index', ['page' => 'a']]],
 | 
						||
                    ['post', false],
 | 
						||
                    ['posts', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with param requirement',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with multiple params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1-a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple params with special chars',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page-number:\d+>/<per_page:\d+>/<author.login>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1/25/yiiuser', ['post/index', ['page-number' => '1', 'per_page' => '25', 'author.login' => 'yiiuser']]],
 | 
						||
                    ['post/1/25', false],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple params with special chars',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<1page-number:\d+>/<-per_page:\d+>/<_author.login>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1/25/yiiuser', ['post/index', ['1page-number' => '1', '-per_page' => '25', '_author.login' => 'yiiuser']]],
 | 
						||
                    ['post/1/25', false],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with optional param',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1/a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/2/a', ['post/index', ['page' => '2', 'tag' => 'a']]],
 | 
						||
                    ['post/a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'tag' => '1']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with optional param not in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'tag' => '1']]],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple optional params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>/<sort:yes|no>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'sort' => 'yes'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1/a/yes', ['post/index', ['page' => '1', 'tag' => 'a', 'sort' => 'yes']]],
 | 
						||
                    ['post/1/a/no', ['post/index', ['page' => '1', 'tag' => 'a', 'sort' => 'no']]],
 | 
						||
                    ['post/2/a/no', ['post/index', ['page' => '2', 'tag' => 'a', 'sort' => 'no']]],
 | 
						||
                    ['post/2/a', ['post/index', ['page' => '2', 'tag' => 'a', 'sort' => 'yes']]],
 | 
						||
                    ['post/a/no', ['post/index', ['page' => '1', 'tag' => 'a', 'sort' => 'no']]],
 | 
						||
                    ['post/a', ['post/index', ['page' => '1', 'tag' => 'a', 'sort' => 'yes']]],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param and required param separated by dashes',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1-a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/2-a', ['post/index', ['page' => '2', 'tag' => 'a']]],
 | 
						||
                    ['post/-a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post-a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param at the beginning',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language>/<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'two optional params at the beginning',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>/<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'two optional params at the beginning followed by placeholder',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>/<category>/test',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['books/test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/books/test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'two optional params at the beginning separated by placeholder',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>/test/<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/test', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['test/books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en/test/books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'three optional params at the beginning separated by placeholder',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>/test/<category>/<id:\d+>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books', 'id' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['test', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en/test', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['test/books', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en/test/books', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'two optional params at the beginning separated by dash',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>-<category>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['-', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en-', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['-books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                    ['en-books', ['site/category', ['language' => 'en', 'category' => 'books']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'three optional params at the beginning separated by dash',
 | 
						||
                [
 | 
						||
                    'pattern' => '<language:(en|pl)>-<category>/<id:\d+>',
 | 
						||
                    'route' => 'site/category',
 | 
						||
                    'defaults' => ['language' => 'en', 'category' => 'books', 'id' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['-', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en-', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['-books', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en-books', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en-books/1', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 1]]],
 | 
						||
                    ['en-books/2', ['site/category', ['language' => 'en', 'category' => 'books', 'id' => 2]]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'optional param at the end',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<tag>/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/a/1', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/a/2', ['post/index', ['page' => '2', 'tag' => 'a']]],
 | 
						||
                    ['post/a', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/2', ['post/index', ['page' => '1', 'tag' => '2']]],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'consecutive optional params',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'tag' => 'a'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/2/b', ['post/index', ['page' => '2', 'tag' => 'b']]],
 | 
						||
                    ['post/2', ['post/index', ['page' => '2', 'tag' => 'a']]],
 | 
						||
                    ['post', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post/b', ['post/index', ['page' => '1', 'tag' => 'b']]],
 | 
						||
                    ['post//b', false, ['post/index', ['page' => '1', 'tag' => 'b']]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'consecutive optional params separated by dash',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>-<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'tag' => 'a'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/2-b', ['post/index', ['page' => '2', 'tag' => 'b']]],
 | 
						||
                    ['post/2-', ['post/index', ['page' => '2', 'tag' => 'a']]],
 | 
						||
                    ['post/-b', ['post/index', ['page' => '1', 'tag' => 'b']]],
 | 
						||
                    ['post/-', ['post/index', ['page' => '1', 'tag' => 'a']]],
 | 
						||
                    ['post', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has parameters',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => [],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['post/index', []]],
 | 
						||
                    ['module/post/index', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has parameters with regex',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller:post|comment>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => [],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/index', ['post/index', []]],
 | 
						||
                    ['comment/index', ['comment/index', []]],
 | 
						||
                    ['test/index', false],
 | 
						||
                    ['post', false],
 | 
						||
                    ['module/post/index', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'route has default parameter',
 | 
						||
                [
 | 
						||
                    'pattern' => '<controller:post|comment>/<action>',
 | 
						||
                    'route' => '<controller>/<action>',
 | 
						||
                    'defaults' => ['action' => 'index'],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/view', ['post/view', []]],
 | 
						||
                    ['comment/view', ['comment/view', []]],
 | 
						||
                    ['test/view', false],
 | 
						||
                    ['post', ['post/index', []]],
 | 
						||
                    ['posts', false],
 | 
						||
                    ['test', false],
 | 
						||
                    ['index', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'empty pattern with suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '.html',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['', ['post/index', []]],
 | 
						||
                    ['.html', false],
 | 
						||
                    ['a.html', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'regular pattern with suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '.html',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['posts.html', ['post/index', []]],
 | 
						||
                    ['posts', false],
 | 
						||
                    ['posts.HTML', false],
 | 
						||
                    ['a.html', false],
 | 
						||
                    ['a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'empty pattern with slash suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '/',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['', ['post/index', []]],
 | 
						||
                    ['a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'regular pattern with slash suffix',
 | 
						||
                [
 | 
						||
                    'pattern' => 'posts',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'suffix' => '/',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['posts/', ['post/index', []]],
 | 
						||
                    ['posts', false, ['post/index', []]],
 | 
						||
                    ['a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'host' => 'http://<lang:en|fr>.example.com',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => 'http://<lang:en|fr>.example.com/post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'host info + defaults', // https://github.com/yiisoft/yii2/issues/6871
 | 
						||
                [
 | 
						||
                    'pattern' => 'http://en.example.com/<page>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['', ['post/index', ['page' => 1]]],
 | 
						||
                    ['2', ['post/index', ['page' => 2]]],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with relative host info',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'host' => '//<lang:en|fr>.example.com',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with relative host info in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => '//<lang:en|fr>.example.com/post/<page:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                [
 | 
						||
                    ['post/1', ['post/index', ['page' => '1', 'lang' => 'en']]],
 | 
						||
                    ['post/a', false],
 | 
						||
                    ['post/1/a', false],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
        ];
 | 
						||
    }
 | 
						||
 | 
						||
    protected function getTestsForToString()
 | 
						||
    {
 | 
						||
        return [
 | 
						||
            [
 | 
						||
                'empty pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => '',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                '/',
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'multiple params with special chars',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page-number:\d+>/<per_page:\d+>/<author.login>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                'post/<page-number:\d+>/<per_page:\d+>/<author.login>',
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info',
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                    'host' => 'http://<lang:en|fr>.example.com',
 | 
						||
                ],
 | 
						||
                'http://<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with host info in pattern',
 | 
						||
                [
 | 
						||
                    'pattern' => 'http://<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1],
 | 
						||
                ],
 | 
						||
                'http://<lang:en|fr>.example.com/post/<page:\d+>/<tag>',
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with verb',
 | 
						||
                [
 | 
						||
                    'verb' => ['POST'],
 | 
						||
                    'pattern' => 'post/<id:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                'POST post/<id:\d+>',
 | 
						||
            ],
 | 
						||
            [
 | 
						||
                'with verbs',
 | 
						||
                [
 | 
						||
                    'verb' => ['PUT', 'POST'],
 | 
						||
                    'pattern' => 'post/<id:\d+>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                ],
 | 
						||
                'PUT,POST post/<id:\d+>',
 | 
						||
            ],
 | 
						||
        ];
 | 
						||
    }
 | 
						||
 | 
						||
    /**
 | 
						||
     * @dataProvider testGetCreateUrlStatusProvider
 | 
						||
     * @param array $config
 | 
						||
     * @param array $tests
 | 
						||
     */
 | 
						||
    public function testGetCreateUrlStatus($config, $tests)
 | 
						||
    {
 | 
						||
        foreach ($tests as $test) {
 | 
						||
            list($route, $params, $expected, $status) = $test;
 | 
						||
 | 
						||
            $this->mockWebApplication();
 | 
						||
            Yii::$app->set('request', new Request(['hostInfo' => 'http://example.com', 'scriptUrl' => '/index.php']));
 | 
						||
 | 
						||
            $manager = new UrlManager([
 | 
						||
                'cache' => null,
 | 
						||
            ]);
 | 
						||
            $rule = new UrlRule($config);
 | 
						||
            $errorMessage = 'Failed test: ' . VarDumper::dumpAsString($test);
 | 
						||
            $this->assertSame($expected, $rule->createUrl($manager, $route, $params), $errorMessage);
 | 
						||
            $this->assertNotNull($status, $errorMessage);
 | 
						||
            if ($status > 0) {
 | 
						||
                $this->assertSame($status, $rule->getCreateUrlStatus() & $status, $errorMessage);
 | 
						||
            } else {
 | 
						||
                $this->assertSame($status, $rule->getCreateUrlStatus(), $errorMessage);
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    /**
 | 
						||
     * Provides test cases for getCreateUrlStatus() method.
 | 
						||
     *
 | 
						||
     * - first param are properties of the UrlRule
 | 
						||
     * - second param is an array of test cases, containing two element arrays:
 | 
						||
     *   - first element is the route to create
 | 
						||
     *   - second element is the array of params
 | 
						||
     *   - third element is the expected URL
 | 
						||
     *   - fourth element is the expected result of getCreateUrlStatus() method
 | 
						||
     */
 | 
						||
    public function testGetCreateUrlStatusProvider()
 | 
						||
    {
 | 
						||
        return [
 | 
						||
            'route' => [
 | 
						||
                // rule properties
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>/<sort:yes|no>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'sort' => 'yes'],
 | 
						||
                ],
 | 
						||
                // test cases: route, params, expected, createStatus
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], 'post/a', UrlRule::CREATE_STATUS_SUCCESS],
 | 
						||
                    ['module/post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], false, UrlRule::CREATE_STATUS_ROUTE_MISMATCH],
 | 
						||
                    ['post/index/action', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], false, UrlRule::CREATE_STATUS_ROUTE_MISMATCH],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            'optional params' => [
 | 
						||
                // rule properties
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>/<sort:yes|no>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'sort' => 'yes'],
 | 
						||
                ],
 | 
						||
                // test cases: route, params, expected, createStatus
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], 'post/a', UrlRule::CREATE_STATUS_SUCCESS],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a', 'sort' => 'yes'], 'post/2/a', UrlRule::CREATE_STATUS_SUCCESS],
 | 
						||
                    ['post/index', ['page' => 2, 'tag' => 'a', 'sort' => 'no'], 'post/2/a/no', UrlRule::CREATE_STATUS_SUCCESS],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'no'], 'post/a/no', UrlRule::CREATE_STATUS_SUCCESS],
 | 
						||
                    [
 | 
						||
                        'post/index',
 | 
						||
                        ['page' => 1, 'tag' => 'a', 'sort' => 'no', 'category' => 'my-category'],
 | 
						||
                        'post/a/no?category=my-category',
 | 
						||
                        UrlRule::CREATE_STATUS_SUCCESS,
 | 
						||
                    ],
 | 
						||
                    ['post/index', ['page' => 1], false, UrlRule::CREATE_STATUS_PARAMS_MISMATCH],
 | 
						||
                    ['post/index', ['page' => '1abc', 'tag' => 'a'], false, UrlRule::CREATE_STATUS_PARAMS_MISMATCH],
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'YES'], false, UrlRule::CREATE_STATUS_PARAMS_MISMATCH],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
            'parsing only' => [
 | 
						||
                // rule properties
 | 
						||
                [
 | 
						||
                    'pattern' => 'post/<page:\d+>/<tag>/<sort:yes|no>',
 | 
						||
                    'route' => 'post/index',
 | 
						||
                    'defaults' => ['page' => 1, 'sort' => 'yes'],
 | 
						||
                    'mode' => UrlRule::PARSING_ONLY,
 | 
						||
                ],
 | 
						||
                // test cases: route, params, expected, createStatus
 | 
						||
                [
 | 
						||
                    ['post/index', ['page' => 1, 'tag' => 'a', 'sort' => 'yes'], false, UrlRule::CREATE_STATUS_PARSING_ONLY],
 | 
						||
                ],
 | 
						||
            ],
 | 
						||
        ];
 | 
						||
    }
 | 
						||
}
 |