Commit 24e086de by Qiang Xue

Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule`

parent e99d5dba
...@@ -149,6 +149,7 @@ Yii Framework 2 Change Log ...@@ -149,6 +149,7 @@ Yii Framework 2 Change Log
- Enh: Added `Pagination::getLinks()` (qiangxue) - Enh: Added `Pagination::getLinks()` (qiangxue)
- Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue) - Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue)
- Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe) - Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe)
- Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue)
- Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue) - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue) - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use Yii;
use yii\base\Object;
/**
* CompositeUrlRule represents a collection of related URL rules.
*
* These URL rules are typically created for a common purpose (e.g. RESTful API for a resource).
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class CompositeUrlRule extends Object implements UrlRuleInterface
{
/**
* @var UrlRuleInterface[] the URL rules contained in this composite rule.
* This property is set in [[init()]] by the return value of [[createRules()]].
*/
protected $rules = [];
/**
* Creates the URL rules that should be contained within this composite rule.
* @return UrlRuleInterface[] the URL rules
*/
abstract protected function createRules();
/**
* @inheritdoc
*/
public function init()
{
parent::init();
$this->rules = $this->createRules();
}
/**
* @inheritdoc
*/
public function parseRequest($manager, $request)
{
foreach ($this->rules as $rule) {
/** @var \yii\web\UrlRule $rule */
if (($result = $rule->parseRequest($manager, $request)) !== false) {
Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
return $result;
}
}
return false;
}
/**
* @inheritdoc
*/
public function createUrl($manager, $route, $params)
{
foreach ($this->rules as $rule) {
/** @var \yii\web\UrlRule $rule */
if (($url = $rule->createUrl($manager, $route, $params)) !== false) {
return $url;
}
}
return false;
}
}
...@@ -9,6 +9,7 @@ namespace yii\web; ...@@ -9,6 +9,7 @@ namespace yii\web;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\caching\Cache; use yii\caching\Cache;
/** /**
...@@ -156,17 +157,22 @@ class UrlManager extends Component ...@@ -156,17 +157,22 @@ class UrlManager extends Component
} }
$rules = []; $rules = [];
$verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';
foreach ($this->rules as $key => $rule) { foreach ($this->rules as $key => $rule) {
if (!is_array($rule)) { if (!is_array($rule)) {
$rule = ['route' => $rule]; $rule = ['route' => $rule];
if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) { if (preg_match("/^((?:($verbs),)*($verbs))\\s+(.*)$/", $key, $matches)) {
$rule['verb'] = explode(',', $matches[1]); $rule['verb'] = explode(',', $matches[1]);
$rule['mode'] = UrlRule::PARSING_ONLY; $rule['mode'] = UrlRule::PARSING_ONLY;
$key = $matches[4]; $key = $matches[4];
} }
$rule['pattern'] = $key; $rule['pattern'] = $key;
} }
$rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule)); $rule = Yii::createObject(array_merge($this->ruleConfig, $rule));
if (!$rule instanceof UrlRuleInterface) {
throw new InvalidConfigException('URL rule class must implement UrlRuleInterface.');
}
$rules[] = $rule;
} }
$this->rules = $rules; $this->rules = $rules;
...@@ -188,7 +194,6 @@ class UrlManager extends Component ...@@ -188,7 +194,6 @@ class UrlManager extends Component
/** @var UrlRule $rule */ /** @var UrlRule $rule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
if (($result = $rule->parseRequest($this, $request)) !== false) { if (($result = $rule->parseRequest($this, $request)) !== false) {
Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
return $result; return $result;
} }
} }
...@@ -245,7 +250,7 @@ class UrlManager extends Component ...@@ -245,7 +250,7 @@ class UrlManager extends Component
/** @var UrlRule $rule */ /** @var UrlRule $rule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
if (($url = $rule->createUrl($this, $route, $params)) !== false) { if (($url = $rule->createUrl($this, $route, $params)) !== false) {
if ($rule->host !== null) { if (strpos($url, '://') !== false) {
if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) { if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) {
return substr($url, 0, $pos) . $baseUrl . substr($url, $pos); return substr($url, 0, $pos) . $baseUrl . substr($url, $pos);
} else { } else {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace yii\web; namespace yii\web;
use Yii;
use yii\base\Object; use yii\base\Object;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
...@@ -26,7 +27,7 @@ use yii\base\InvalidConfigException; ...@@ -26,7 +27,7 @@ use yii\base\InvalidConfigException;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class UrlRule extends Object class UrlRule extends Object implements UrlRuleInterface
{ {
/** /**
* Set [[mode]] with this value to mark that this rule is for URL parsing only * Set [[mode]] with this value to mark that this rule is for URL parsing only
...@@ -47,7 +48,7 @@ class UrlRule extends Object ...@@ -47,7 +48,7 @@ class UrlRule extends Object
*/ */
public $pattern; public $pattern;
/** /**
* @var string the pattern used to parse and create the host info part of a URL. * @var string the pattern used to parse and create the host info part of a URL (e.g. `http://example.com`).
* @see pattern * @see pattern
*/ */
public $host; public $host;
...@@ -127,7 +128,8 @@ class UrlRule extends Object ...@@ -127,7 +128,8 @@ class UrlRule extends Object
$this->pattern = trim($this->pattern, '/'); $this->pattern = trim($this->pattern, '/');
if ($this->host !== null) { if ($this->host !== null) {
$this->pattern = rtrim($this->host, '/') . rtrim('/' . $this->pattern, '/') . '/'; $this->host = rtrim($this->host, '/');
$this->pattern = rtrim($this->host . '/' . $this->pattern, '/');
} elseif ($this->pattern === '') { } elseif ($this->pattern === '') {
$this->_template = ''; $this->_template = '';
$this->pattern = '#^$#u'; $this->pattern = '#^$#u';
...@@ -157,7 +159,7 @@ class UrlRule extends Object ...@@ -157,7 +159,7 @@ class UrlRule extends Object
foreach ($matches as $match) { foreach ($matches as $match) {
$name = $match[1][0]; $name = $match[1][0];
$pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+'; $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
if (isset($this->defaults[$name])) { if (array_key_exists($name, $this->defaults)) {
$length = strlen($match[0][0]); $length = strlen($match[0][0]);
$offset = $match[0][1]; $offset = $match[0][1];
if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') { if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') {
...@@ -243,6 +245,9 @@ class UrlRule extends Object ...@@ -243,6 +245,9 @@ class UrlRule extends Object
} else { } else {
$route = $this->route; $route = $this->route;
} }
Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__);
return [$route, $params]; return [$route, $params];
} }
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* UrlRuleInterface is the interface that should be implemented URL rule classes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
interface UrlRuleInterface
{
/**
* Parses the given request and returns the corresponding route and parameters.
* @param UrlManager $manager the URL manager
* @param Request $request the request component
* @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 parseRequest($manager, $request);
/**
* 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);
}
...@@ -12,6 +12,12 @@ use yiiunit\TestCase; ...@@ -12,6 +12,12 @@ use yiiunit\TestCase;
*/ */
class UrlRuleTest extends TestCase class UrlRuleTest extends TestCase
{ {
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testCreateUrl() public function testCreateUrl()
{ {
$manager = new UrlManager(['cache' => null]); $manager = new UrlManager(['cache' => null]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment