You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
5.0KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\web;
  8. use Yii;
  9. use yii\base\Object;
  10. use yii\base\InvalidConfigException;
  11. /**
  12. * UrlNormalizer normalizes URLs for [[UrlManager]] and [[UrlRule]].
  13. *
  14. * @author Robert Korulczyk <robert@korulczyk.pl>
  15. * @author Cronfy <cronfy@gmail.com>
  16. * @since 2.0.10
  17. */
  18. class UrlNormalizer extends Object
  19. {
  20. /**
  21. * Represents permament redirection during route normalization.
  22. * @see https://en.wikipedia.org/wiki/HTTP_301
  23. */
  24. const ACTION_REDIRECT_PERMANENT = 301;
  25. /**
  26. * Represents temporary redirection during route normalization.
  27. * @see https://en.wikipedia.org/wiki/HTTP_302
  28. */
  29. const ACTION_REDIRECT_TEMPORARY = 302;
  30. /**
  31. * Represents showing 404 error page during route normalization.
  32. * @see https://en.wikipedia.org/wiki/HTTP_404
  33. */
  34. const ACTION_NOT_FOUND = 404;
  35. /**
  36. * @var boolean whether slashes should be collapsed, for example `site///index` will be
  37. * converted into `site/index`
  38. */
  39. public $collapseSlashes = true;
  40. /**
  41. * @var boolean whether trailing slash should be normalized according to the suffix settings
  42. * of the rule
  43. */
  44. public $normalizeTrailingSlash = true;
  45. /**
  46. * @var integer|callable|null action to perform during route normalization.
  47. * Available options are:
  48. * - `null` - no special action will be performed
  49. * - `301` - the request should be redirected to the normalized URL using
  50. * permanent redirection
  51. * - `302` - the request should be redirected to the normalized URL using
  52. * temporary redirection
  53. * - `404` - [[NotFoundHttpException]] will be thrown
  54. * - `callable` - custom user callback, for example:
  55. *
  56. * ```php
  57. * function ($route, $normalizer) {
  58. * // use custom action for redirections
  59. * $route[1]['oldRoute'] = $route[0];
  60. * $route[0] = 'site/redirect';
  61. * return $route;
  62. * }
  63. * ```
  64. */
  65. public $action = self::ACTION_REDIRECT_PERMANENT;
  66. /**
  67. * Performs normalization action for the specified $route.
  68. * @param array $route route for normalization
  69. * @return array normalized route
  70. * @throws InvalidConfigException if invalid normalization action is used.
  71. * @throws UrlNormalizerRedirectException if normalization requires redirection.
  72. * @throws NotFoundHttpException if normalization suggests action matching route does not exist.
  73. */
  74. public function normalizeRoute($route)
  75. {
  76. if ($this->action === null) {
  77. return $route;
  78. } elseif ($this->action === static::ACTION_REDIRECT_PERMANENT || $this->action === static::ACTION_REDIRECT_TEMPORARY) {
  79. throw new UrlNormalizerRedirectException([$route[0]] + $route[1], $this->action);
  80. } elseif ($this->action === static::ACTION_NOT_FOUND) {
  81. throw new NotFoundHttpException(Yii::t('yii', 'Page not found.'));
  82. } elseif (is_callable($this->action)) {
  83. return call_user_func($this->action, $route, $this);
  84. }
  85. throw new InvalidConfigException('Invalid normalizer action.');
  86. }
  87. /**
  88. * Normalizes specified pathInfo.
  89. * @param string $pathInfo pathInfo for normalization
  90. * @param string $suffix current rule suffix
  91. * @param boolean $normalized if specified, this variable will be set to `true` if $pathInfo
  92. * was changed during normalization
  93. * @return string normalized pathInfo
  94. */
  95. public function normalizePathInfo($pathInfo, $suffix, &$normalized = false)
  96. {
  97. if (empty($pathInfo)) {
  98. return $pathInfo;
  99. }
  100. $sourcePathInfo = $pathInfo;
  101. if ($this->collapseSlashes) {
  102. $pathInfo = $this->collapseSlashes($pathInfo);
  103. }
  104. if ($this->normalizeTrailingSlash === true) {
  105. $pathInfo = $this->normalizeTrailingSlash($pathInfo, $suffix);
  106. }
  107. $normalized = $sourcePathInfo !== $pathInfo;
  108. return $pathInfo;
  109. }
  110. /**
  111. * Collapse consecutive slashes in $pathInfo, for example converts `site///index` into `site/index`.
  112. * @param string $pathInfo raw path info.
  113. * @return string normalized path info.
  114. */
  115. protected function collapseSlashes($pathInfo)
  116. {
  117. return ltrim(preg_replace('#/{2,}#', '/', $pathInfo), '/');
  118. }
  119. /**
  120. * Adds or removes trailing slashes from $pathInfo depending on whether the $suffix has a
  121. * trailing slash or not.
  122. * @param string $pathInfo raw path info.
  123. * @param string $suffix
  124. * @return string normalized path info.
  125. */
  126. protected function normalizeTrailingSlash($pathInfo, $suffix)
  127. {
  128. if (substr($suffix, -1) === '/' && substr($pathInfo, -1) !== '/') {
  129. $pathInfo .= '/';
  130. } elseif (substr($suffix, -1) !== '/' && substr($pathInfo, -1) === '/') {
  131. $pathInfo = rtrim($pathInfo, '/');
  132. }
  133. return $pathInfo;
  134. }
  135. }