From 12fbb0f71d2357df5d4ad776160e3b34fafeb8f7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Feb 2013 19:51:18 -0500 Subject: [PATCH] url WIP --- framework/web/UrlManager.php | 20 ++++- framework/web/UrlRule.php | 67 +++++++++++++--- tests/unit/framework/web/UrlRuleTest.php | 133 ++++++++++++++++++++++++++++++- 3 files changed, 207 insertions(+), 13 deletions(-) diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php index e03f086..8f927e8 100644 --- a/framework/web/UrlManager.php +++ b/framework/web/UrlManager.php @@ -27,7 +27,7 @@ class UrlManager extends Component * @var string the URL suffix used when in 'path' format. * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. Defaults to empty. */ - public $urlSuffix = ''; + public $suffix; /** * @var boolean whether to show entry script name in the constructed URL. Defaults to true. */ @@ -122,7 +122,7 @@ class UrlManager extends Component /** @var $rule UrlRule */ foreach ($this->rules as $rule) { - if (($url = $rule->createUrl($route, $params)) !== false) { + if (($url = $rule->createUrl($this, $route, $params)) !== false) { return $this->getBaseUrl() . $url . $anchor; } } @@ -144,4 +144,20 @@ class UrlManager extends Component { $this->_baseUrl = trim($value, '/'); } + + /** + * Removes the URL suffix from path info. + * @param string $pathInfo path info part in the URL + * @param string $suffix the URL suffix to be removed + * @return string path info with URL suffix removed. + */ + public function removeSuffix($pathInfo, $suffix) + { + $n = strlen($suffix); + if ($n > 0 && substr($pathInfo, -$n) === $suffix) { + return substr($pathInfo, 0, -$n); + } else { + return $pathInfo; + } + } } diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php index f11fd16..471712b 100644 --- a/framework/web/UrlRule.php +++ b/framework/web/UrlRule.php @@ -20,6 +20,15 @@ use yii\base\Object; class UrlRule extends Object { /** + * Set [[mode]] with this value to mark that this rule is for URL parsing only + */ + const PARSING_ONLY = 1; + /** + * Set [[mode]] with this value to mark that this rule is for URL creation only + */ + const CREATION_ONLY = 2; + + /** * @var string regular expression used to parse a URL */ public $pattern; @@ -34,11 +43,6 @@ class UrlRule extends Object */ public $defaults = array(); /** - * @var boolean whether this rule is only used for request parsing. - * Defaults to false, meaning the rule is used for both URL parsing and creation. - */ - public $parsingOnly = false; - /** * @var string the URL suffix used for this rule. * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. * If not, the value of [[UrlManager::suffix]] will be used. @@ -49,7 +53,6 @@ class UrlRule extends Object * Use array to represent multiple verbs that this rule may match. * If this property is not set, the rule can match any verb. * Note that this property is only used when parsing a request. It is ignored for URL creation. - * @see parsingOnly */ public $verb; /** @@ -57,6 +60,14 @@ class UrlRule extends Object * If not set, it means the host info is ignored. */ public $hostInfo; + /** + * @var integer a value indicating if this rule should be used for both URL parsing and creation, + * parsing only, or creation only. + * If not set, it means the rule is both URL parsing and creation. + * If it is [[PARSING_ONLY]], the rule is for URL parsing only. + * If it is [[CREATION_ONLY]], the rule is for URL creation only. + */ + public $mode; /** * @var string the template for generating a new URL. This is derived from [[pattern]] and is used in generating URL. @@ -138,12 +149,38 @@ class UrlRule extends Object } } - public function parseUrl($pathInfo) + /** + * Parses the given path info and returns the corresponding route and parameters. + * @param UrlManager $manager the URL manager + * @param string $pathInfo the path info to be parsed. It should not have slashes at the beginning or the end. + * @return array|boolean the parsing result. The route and the parameters are returned as an array. + * If false, it means this rule cannot be used to parse this path info. + */ + public function parseUrl($manager, $pathInfo) { + if ($this->mode === self::CREATION_ONLY) { + return false; + } + if ($this->verb !== null && !in_array(\Yii::$app->getRequest()->verb, $this->verb, true)) { return false; } + $suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix); + if ($suffix !== '' && $pathInfo !== '') { + $n = strlen($suffix); + if (substr($pathInfo, -$n) === $suffix) { + $pathInfo = substr($pathInfo, 0, -$n); + if ($pathInfo === '') { + // suffix alone is not allowed + return false; + } + } elseif ($suffix !== '/') { + // we allow the ending '/' to be optional if it is a suffix + return false; + } + } + if (!preg_match($this->pattern, $pathInfo, $matches)) { return false; } @@ -170,9 +207,16 @@ class UrlRule extends Object return array($route, $params); } - public function createUrl($route, $params) + /** + * Creates a URL according to the given route and parameters. + * @param UrlManager $manager the URL manager + * @param string $route the route. It should not have slashes at the beginning or the end. + * @param array $params the parameters + * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL. + */ + public function createUrl($manager, $route, $params) { - if ($this->parsingOnly) { + if ($this->mode === self::PARSING_ONLY) { return false; } @@ -225,6 +269,11 @@ class UrlRule extends Object if (strpos($url, '//') !== false) { $url = preg_replace('#/+#', '/', $url); } + + if ($url !== '') { + $url .= ($this->suffix === null ? $manager->suffix : $this->suffix); + } + if ($params !== array()) { $url .= '?' . http_build_query($params); } diff --git a/tests/unit/framework/web/UrlRuleTest.php b/tests/unit/framework/web/UrlRuleTest.php index e9148c7..fd9a8bd 100644 --- a/tests/unit/framework/web/UrlRuleTest.php +++ b/tests/unit/framework/web/UrlRuleTest.php @@ -2,19 +2,21 @@ namespace yiiunit\framework\web; +use yii\web\UrlManager; use yii\web\UrlRule; class UrlRuleTest extends \yiiunit\TestCase { public function testCreateUrl() { + $manager = new UrlManager; $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($route, $params); + $url = $rule->createUrl($manager, $route, $params); $this->assertEquals($expected, $url, "Test#$i-$j: $name"); } } @@ -22,6 +24,7 @@ class UrlRuleTest extends \yiiunit\TestCase public function testParseUrl() { + $manager = new UrlManager; $suites = $this->getTestsForParseUrl(); foreach ($suites as $i => $suite) { list ($name, $config, $tests) = $suite; @@ -30,7 +33,7 @@ class UrlRuleTest extends \yiiunit\TestCase $pathInfo = $test[0]; $route = $test[1]; $params = isset($test[2]) ? $test[2] : array(); - $result = $rule->parseUrl($pathInfo); + $result = $rule->parseUrl($manager, $pathInfo); if ($route === false) { $this->assertFalse($result, "Test#$i-$j: $name"); } else { @@ -75,6 +78,17 @@ class UrlRuleTest extends \yiiunit\TestCase ), ), array( + 'parsing only', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'mode' => UrlRule::PARSING_ONLY, + ), + array( + array('post/index', array(), false), + ), + ), + array( 'with param', array( 'pattern' => 'post/', @@ -259,6 +273,58 @@ class UrlRuleTest extends \yiiunit\TestCase array('post/index', array('page' => 1), 'post?page=1'), ), ), + array( + 'empty pattern with suffix', + array( + 'pattern' => '', + 'route' => 'post/index', + 'suffix' => '.html', + ), + array( + array('post/index', array(), ''), + array('comment/index', array(), false), + array('post/index', array('page' => 1), '?page=1'), + ), + ), + array( + 'regular pattern with suffix', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '.html', + ), + array( + array('post/index', array(), 'posts.html'), + array('comment/index', array(), false), + array('post/index', array('page' => 1), 'posts.html?page=1'), + ), + ), + array( + 'empty pattern with slash suffix', + array( + 'pattern' => '', + 'route' => 'post/index', + 'suffix' => '/', + ), + array( + array('post/index', array(), ''), + array('comment/index', array(), false), + array('post/index', array('page' => 1), '?page=1'), + ), + ), + array( + 'regular pattern with slash suffix', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '/', + ), + array( + array('post/index', array(), 'posts/'), + array('comment/index', array(), false), + array('post/index', array('page' => 1), 'posts/?page=1'), + ), + ), ); } @@ -295,6 +361,17 @@ class UrlRuleTest extends \yiiunit\TestCase ), ), array( + 'creation only', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'mode' => UrlRule::CREATION_ONLY, + ), + array( + array('posts', false), + ), + ), + array( 'with param', array( 'pattern' => 'post/', @@ -479,6 +556,58 @@ class UrlRuleTest extends \yiiunit\TestCase array('index', false), ), ), + array( + 'empty pattern with suffix', + array( + 'pattern' => '', + 'route' => 'post/index', + 'suffix' => '.html', + ), + array( + array('', 'post/index'), + array('.html', false), + array('a.html', false), + ), + ), + array( + 'regular pattern with suffix', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '.html', + ), + array( + array('posts.html', 'post/index'), + array('posts', false), + array('posts.HTML', false), + array('a.html', false), + array('a', false), + ), + ), + array( + 'empty pattern with slash suffix', + array( + 'pattern' => '', + 'route' => 'post/index', + 'suffix' => '/', + ), + array( + array('', 'post/index'), + array('a', false), + ), + ), + array( + 'regular pattern with slash suffix', + array( + 'pattern' => 'posts', + 'route' => 'post/index', + 'suffix' => '/', + ), + array( + array('posts', 'post/index'), + array('a', false), + ), + ), ); } }