value * - array config * * The following features are tested: * - named parameters * - as query params * - as controller/actions '//' => '/', * - Rules with Server Names * - with protocol * - without protocol i.e protocol relative, see https://github.com/yiisoft/yii2/pull/12697 * - with parameters * - with suffix * - with default values * - with HTTP methods * * - Adding rules dynamically * - Test custom rules that only implement the interface * * NOTE: if a test is added here, you probably also need to add one in UrlManagerCreateUrlTest. * * @group web */ class UrlManagerParseUrlTest extends TestCase { protected function getUrlManager($config = []) { // in this test class, all tests have enablePrettyUrl enabled. $config['enablePrettyUrl'] = true; // normalizer is tested in UrlNormalizerTest $config['normalizer'] = false; return new UrlManager(array_merge([ 'cache' => null, ], $config)); } protected function getRequest($pathInfo, $hostInfo = 'http://www.example.com', $method = 'GET', $config = []) { $config['pathInfo'] = $pathInfo; $config['hostInfo'] = $hostInfo; $_POST['_method'] = $method; return new Request($config); } protected function tearDown() { unset($_POST['_method']); parent::tearDown(); } public function testWithoutRules() { $manager = $this->getUrlManager(); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertEquals(['', []], $result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertEquals(['site/index', []], $result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertEquals(['module/site/index', []], $result); // pathinfo with trailing slashes $result = $manager->parseRequest($this->getRequest('module/site/index/')); $this->assertEquals(['module/site/index/', []], $result); } public function testWithoutRulesStrict() { $manager = $this->getUrlManager(); $manager->enableStrictParsing = true; // empty pathinfo $this->assertFalse($manager->parseRequest($this->getRequest(''))); // normal pathinfo $this->assertFalse($manager->parseRequest($this->getRequest('site/index'))); // pathinfo with module $this->assertFalse($manager->parseRequest($this->getRequest('module/site/index'))); // pathinfo with trailing slashes $this->assertFalse($manager->parseRequest($this->getRequest('module/site/index/'))); } public function suffixProvider() { return [ ['.html'], ['/'], ]; } /** * @dataProvider suffixProvider * @param string $suffix */ public function testWithoutRulesWithSuffix($suffix) { $manager = $this->getUrlManager(['suffix' => $suffix]); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertEquals(['', []], $result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("site/index$suffix")); $this->assertEquals(['site/index', []], $result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("module/site/index$suffix")); $this->assertEquals(['module/site/index', []], $result); // pathinfo with trailing slashes if ($suffix !== '/') { $result = $manager->parseRequest($this->getRequest('module/site/index/')); $this->assertFalse($result); } $result = $manager->parseRequest($this->getRequest("module/site/index/$suffix")); $this->assertEquals(['module/site/index/', []], $result); } public function testSimpleRules() { $config = [ 'rules' => [ 'post/' => 'post/view', 'posts' => 'post/index', 'book//' => 'book/view', ], ]; $manager = $this->getUrlManager($config); // matching pathinfo $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample')); $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); // trailing slash is significant, no match $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/')); $this->assertEquals(['book/123/this+is+sample/', []], $result); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertEquals(['', []], $result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertEquals(['site/index', []], $result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertEquals(['module/site/index', []], $result); } public function testSimpleRulesStrict() { $config = [ 'rules' => [ 'post/<id:\d+>' => 'post/view', 'posts' => 'post/index', 'book/<id:\d+>/<title>' => 'book/view', ], ]; $manager = $this->getUrlManager($config); $manager->enableStrictParsing = true; // matching pathinfo $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample')); $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); // trailing slash is significant, no match $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/')); $this->assertFalse($result); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertFalse($result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertFalse($result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertFalse($result); } /** * @dataProvider suffixProvider * @param string $suffix */ public function testSimpleRulesWithSuffix($suffix) { $config = [ 'rules' => [ 'post/<id:\d+>' => 'post/view', 'posts' => 'post/index', 'book/<id:\d+>/<title>' => 'book/view', ], 'suffix' => $suffix, ]; $manager = $this->getUrlManager($config); // matching pathinfo $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("book/123/this+is+sample$suffix")); $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); // trailing slash is significant, no match $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/')); if ($suffix === '/') { $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); } else { $this->assertFalse($result); } $result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/$suffix")); $this->assertEquals(['book/123/this+is+sample/', []], $result); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertEquals(['', []], $result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("site/index$suffix")); $this->assertEquals(['site/index', []], $result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("module/site/index$suffix")); $this->assertEquals(['module/site/index', []], $result); } /** * @dataProvider suffixProvider * @param string $suffix */ public function testSimpleRulesWithSuffixStrict($suffix) { $config = [ 'rules' => [ 'post/<id:\d+>' => 'post/view', 'posts' => 'post/index', 'book/<id:\d+>/<title>' => 'book/view', ], 'suffix' => $suffix, ]; $manager = $this->getUrlManager($config); $manager->enableStrictParsing = true; // matching pathinfo $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("book/123/this+is+sample$suffix")); $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); // trailing slash is significant, no match $result = $manager->parseRequest($this->getRequest('book/123/this+is+sample/')); if ($suffix === '/') { $this->assertEquals(['book/view', ['id' => '123', 'title' => 'this+is+sample']], $result); } else { $this->assertFalse($result); } $result = $manager->parseRequest($this->getRequest("book/123/this+is+sample/$suffix")); $this->assertFalse($result); // empty pathinfo $result = $manager->parseRequest($this->getRequest('')); $this->assertFalse($result); // normal pathinfo $result = $manager->parseRequest($this->getRequest('site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("site/index$suffix")); $this->assertFalse($result); // pathinfo with module $result = $manager->parseRequest($this->getRequest('module/site/index')); $this->assertFalse($result); $result = $manager->parseRequest($this->getRequest("module/site/index$suffix")); $this->assertFalse($result); } // TODO implement with hostinfo public function testParseRESTRequest() { $request = new Request(); // pretty URL rules $manager = new UrlManager([ 'enablePrettyUrl' => true, 'showScriptName' => false, 'cache' => null, 'rules' => [ 'PUT,POST post/<id>/<title>' => 'post/create', 'DELETE post/<id>' => 'post/delete', 'post/<id>/<title>' => 'post/view', 'POST/GET' => 'post/get', ], ]); // matching pathinfo GET request $_SERVER['REQUEST_METHOD'] = 'GET'; $request->pathInfo = 'post/123/this+is+sample'; $result = $manager->parseRequest($request); $this->assertEquals(['post/view', ['id' => '123', 'title' => 'this+is+sample']], $result); // matching pathinfo PUT/POST request $_SERVER['REQUEST_METHOD'] = 'PUT'; $request->pathInfo = 'post/123/this+is+sample'; $result = $manager->parseRequest($request); $this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result); $_SERVER['REQUEST_METHOD'] = 'POST'; $request->pathInfo = 'post/123/this+is+sample'; $result = $manager->parseRequest($request); $this->assertEquals(['post/create', ['id' => '123', 'title' => 'this+is+sample']], $result); // no wrong matching $_SERVER['REQUEST_METHOD'] = 'POST'; $request->pathInfo = 'POST/GET'; $result = $manager->parseRequest($request); $this->assertEquals(['post/get', []], $result); // createUrl should ignore REST rules $this->mockApplication([ 'components' => [ 'request' => [ 'hostInfo' => 'http://localhost/', 'baseUrl' => '/app', ], ], ], \yii\web\Application::className()); $this->assertEquals('/app/post/delete?id=123', $manager->createUrl(['post/delete', 'id' => 123])); $this->destroyApplication(); unset($_SERVER['REQUEST_METHOD']); } public function testAppendRules() { $manager = $this->getUrlManager(['rules' => ['post/<id:\d+>' => 'post/view']]); $this->assertCount(1, $manager->rules); $firstRule = $manager->rules[0]; $this->assertInstanceOf('yii\web\UrlRuleInterface', $firstRule); $manager->addRules([ 'posts' => 'post/index', 'book/<id:\d+>/<title>' => 'book/view', ]); $this->assertCount(3, $manager->rules); $this->assertSame($firstRule, $manager->rules[0]); } public function testPrependRules() { $manager = $this->getUrlManager(['rules' => ['post/<id:\d+>' => 'post/view']]); $this->assertCount(1, $manager->rules); $firstRule = $manager->rules[0]; $this->assertInstanceOf('yii\web\UrlRuleInterface', $firstRule); $manager->addRules([ 'posts' => 'post/index', 'book/<id:\d+>/<title>' => 'book/view', ], false); $this->assertCount(3, $manager->rules); $this->assertNotSame($firstRule, $manager->rules[0]); $this->assertSame($firstRule, $manager->rules[2]); } public function testRulesCache() { $arrayCache = new ArrayCache(); $manager = $this->getUrlManager([ 'rules' => ['post/<id:\d+>' => 'post/view'], 'cache' => $arrayCache, ]); $this->assertCount(1, $manager->rules); $firstRule = $manager->rules[0]; $this->assertInstanceOf('yii\web\UrlRuleInterface', $firstRule); $this->assertCount(1, $this->getInaccessibleProperty($arrayCache, '_cache'), 'Cache contains the only one record that represents initial built rules' ); $manager->addRules(['posts' => 'post/index']); $manager->addRules([ 'book/<id:\d+>/<title>' => 'book/view', 'book/<id:\d+>/<author>' => 'book/view' ]); $this->assertCount(4, $manager->rules); $this->assertSame($firstRule, $manager->rules[0]); $this->assertCount(3, $this->getInaccessibleProperty($arrayCache, '_cache'), 'The addRules() method was called twice, adding 3 new rules to the UrlManager, but we have only ' . 'two additional caches: one for each addRules() method call.' ); } public function testRulesCacheIsUsed() { $arrayCache = $this->getMockBuilder('yii\caching\ArrayCache') ->setMethods(['get', 'set']) ->getMock(); $manager = $this->getUrlManager([ 'rules' => ['post/<id:\d+>' => 'post/view'], 'cache' => $arrayCache, ]); $savedRules = $manager->rules; // save rules to "cache" and make sure it is reused $arrayCache->expects($this->exactly(2))->method('get')->willReturn($savedRules); $arrayCache->expects($this->never())->method('set'); for ($i = 0; $i < 2; $i++) { $this->getUrlManager([ 'rules' => ['post/<id:\d+>' => 'post/view'], 'cache' => $arrayCache, ]); } } }