Issues (902)

framework/helpers/BaseUrl.php (17 issues)

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use Yii;
11
use yii\base\InvalidArgumentException;
12
13
/**
14
 * BaseUrl provides concrete implementation for [[Url]].
15
 *
16
 * Do not use BaseUrl. Use [[Url]] instead.
17
 *
18
 * @author Alexander Makarov <[email protected]>
19
 * @since 2.0
20
 */
21
class BaseUrl
22
{
23
    /**
24
     * @var \yii\web\UrlManager URL manager to use for creating URLs
25
     * @since 2.0.8
26
     */
27
    public static $urlManager;
28
29
30
    /**
31
     * Creates a URL for the given route.
32
     *
33
     * This method will use [[\yii\web\UrlManager]] to create a URL.
34
     *
35
     * You may specify the route as a string, e.g., `site/index`. You may also use an array
36
     * if you want to specify additional query parameters for the URL being created. The
37
     * array format must be:
38
     *
39
     * ```php
40
     * // generates: /index.php?r=site/index&param1=value1&param2=value2
41
     * ['site/index', 'param1' => 'value1', 'param2' => 'value2']
42
     * ```
43
     *
44
     * If you want to create a URL with an anchor, you can use the array format with a `#` parameter.
45
     * For example,
46
     *
47
     * ```php
48
     * // generates: /index.php?r=site/index&param1=value1#name
49
     * ['site/index', 'param1' => 'value1', '#' => 'name']
50
     * ```
51
     *
52
     * A route may be either absolute or relative. An absolute route has a leading slash (e.g. `/site/index`),
53
     * while a relative route has none (e.g. `site/index` or `index`). A relative route will be converted
54
     * into an absolute one by the following rules:
55
     *
56
     * - If the route is an empty string, the current [[\yii\web\Controller::route|route]] will be used;
57
     * - If the route contains no slashes at all (e.g. `index`), it is considered to be an action ID
58
     *   of the current controller and will be prepended with [[\yii\web\Controller::uniqueId]];
59
     * - If the route has no leading slash (e.g. `site/index`), it is considered to be a route relative
60
     *   to the current module and will be prepended with the module's [[\yii\base\Module::uniqueId|uniqueId]].
61
     *
62
     * Starting from version 2.0.2, a route can also be specified as an alias. In this case, the alias
63
     * will be converted into the actual route first before conducting the above transformation steps.
64
     *
65
     * Below are some examples of using this method:
66
     *
67
     * ```php
68
     * // /index.php?r=site%2Findex
69
     * echo Url::toRoute('site/index');
70
     *
71
     * // /index.php?r=site%2Findex&src=ref1#name
72
     * echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']);
73
     *
74
     * // https://www.example.com/index.php?r=site%2Findex
75
     * echo Url::toRoute('site/index', true);
76
     *
77
     * // https://www.example.com/index.php?r=site%2Findex
78
     * echo Url::toRoute('site/index', 'https');
79
     *
80
     * // /index.php?r=post%2Findex     assume the alias "@posts" is defined as "post/index"
81
     * echo Url::toRoute('@posts');
82
     * ```
83
     *
84
     * @param string|array $route use a string to represent a route (e.g. `index`, `site/index`),
85
     * or an array to represent a route with query parameters (e.g. `['site/index', 'param1' => 'value1']`).
86
     * @param bool|string $scheme the URI scheme to use in the generated URL:
87
     *
88
     * - `false` (default): generating a relative URL.
89
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
90
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
91
     *   for protocol-relative URL).
92
     *
93
     * @return string the generated URL
94
     * @throws InvalidArgumentException a relative route is given while there is no active controller
95
     */
96 16
    public static function toRoute($route, $scheme = false)
97
    {
98 16
        $route = (array) $route;
99 16
        $route[0] = static::normalizeRoute($route[0]);
100
101 16
        if ($scheme !== false) {
102 3
            return static::getUrlManager()->createAbsoluteUrl($route, is_string($scheme) ? $scheme : null);
103
        }
104
105 15
        return static::getUrlManager()->createUrl($route);
106
    }
107
108
    /**
109
     * Normalizes route and makes it suitable for UrlManager. Absolute routes are staying as is
110
     * while relative routes are converted to absolute ones.
111
     *
112
     * A relative route is a route without a leading slash, such as "view", "post/view".
113
     *
114
     * - If the route is an empty string, the current [[\yii\web\Controller::route|route]] will be used;
115
     * - If the route contains no slashes at all, it is considered to be an action ID
116
     *   of the current controller and will be prepended with [[\yii\web\Controller::uniqueId]];
117
     * - If the route has no leading slash, it is considered to be a route relative
118
     *   to the current module and will be prepended with the module's uniqueId.
119
     *
120
     * Starting from version 2.0.2, a route can also be specified as an alias. In this case, the alias
121
     * will be converted into the actual route first before conducting the above transformation steps.
122
     *
123
     * @param string $route the route. This can be either an absolute route or a relative route.
124
     * @return string normalized route suitable for UrlManager
125
     * @throws InvalidArgumentException a relative route is given while there is no active controller
126
     */
127 16
    protected static function normalizeRoute($route)
128
    {
129 16
        $route = Yii::getAlias((string) $route);
130 16
        if (strncmp($route, '/', 1) === 0) {
0 ignored issues
show
It seems like $route can also be of type false; however, parameter $string1 of strncmp() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

130
        if (strncmp(/** @scrutinizer ignore-type */ $route, '/', 1) === 0) {
Loading history...
131
            // absolute route
132 15
            return ltrim($route, '/');
0 ignored issues
show
It seems like $route can also be of type false; however, parameter $string of ltrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
            return ltrim(/** @scrutinizer ignore-type */ $route, '/');
Loading history...
133
        }
134
135
        // relative route
136 3
        if (Yii::$app->controller === null) {
137 2
            throw new InvalidArgumentException("Unable to resolve the relative route: $route. No active controller is available.");
138
        }
139
140 3
        if (strpos($route, '/') === false) {
0 ignored issues
show
It seems like $route can also be of type false; however, parameter $haystack of strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
        if (strpos(/** @scrutinizer ignore-type */ $route, '/') === false) {
Loading history...
141
            // empty or an action ID
142 3
            return $route === '' ? Yii::$app->controller->getRoute() : Yii::$app->controller->getUniqueId() . '/' . $route;
0 ignored issues
show
Are you sure $route of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

142
            return $route === '' ? Yii::$app->controller->getRoute() : Yii::$app->controller->getUniqueId() . '/' . /** @scrutinizer ignore-type */ $route;
Loading history...
143
        }
144
145
        // relative to module
146 1
        return ltrim(Yii::$app->controller->module->getUniqueId() . '/' . $route, '/');
147
    }
148
149
    /**
150
     * Creates a URL based on the given parameters.
151
     *
152
     * This method is very similar to [[toRoute()]]. The only difference is that this method
153
     * requires a route to be specified as an array only. If a string is given, it will be treated as a URL.
154
     * In particular, if `$url` is
155
     *
156
     * - an array: [[toRoute()]] will be called to generate the URL. For example:
157
     *   `['site/index']`, `['post/index', 'page' => 2]`. Please refer to [[toRoute()]] for more details
158
     *   on how to specify a route.
159
     * - a string with a leading `@`: it is treated as an alias, and the corresponding aliased string
160
     *   will be returned.
161
     * - an empty string: the currently requested URL will be returned;
162
     * - a normal string: it will be returned as is.
163
     *
164
     * When `$scheme` is specified (either a string or `true`), an absolute URL with host info (obtained from
165
     * [[\yii\web\UrlManager::$hostInfo]]) will be returned. If `$url` is already an absolute URL, its scheme
166
     * will be replaced with the specified one.
167
     *
168
     * Below are some examples of using this method:
169
     *
170
     * ```php
171
     * // /index.php?r=site%2Findex
172
     * echo Url::to(['site/index']);
173
     *
174
     * // /index.php?r=site%2Findex&src=ref1#name
175
     * echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']);
176
     *
177
     * // /index.php?r=post%2Findex     assume the alias "@posts" is defined as "/post/index"
178
     * echo Url::to(['@posts']);
179
     *
180
     * // the currently requested URL
181
     * echo Url::to();
182
     *
183
     * // /images/logo.gif
184
     * echo Url::to('@web/images/logo.gif');
185
     *
186
     * // images/logo.gif
187
     * echo Url::to('images/logo.gif');
188
     *
189
     * // https://www.example.com/images/logo.gif
190
     * echo Url::to('@web/images/logo.gif', true);
191
     *
192
     * // https://www.example.com/images/logo.gif
193
     * echo Url::to('@web/images/logo.gif', 'https');
194
     *
195
     * // //www.example.com/images/logo.gif
196
     * echo Url::to('@web/images/logo.gif', '');
197
     * ```
198
     *
199
     *
200
     * @param array|string $url the parameter to be used to generate a valid URL
201
     * @param bool|string $scheme the URI scheme to use in the generated URL:
202
     *
203
     * - `false` (default): generating a relative URL.
204
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
205
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
206
     *   for protocol-relative URL).
207
     *
208
     * @return string the generated URL
209
     * @throws InvalidArgumentException a relative route is given while there is no active controller
210
     */
211 157
    public static function to($url = '', $scheme = false)
212
    {
213 157
        if (is_array($url)) {
214 14
            return static::toRoute($url, $scheme);
215
        }
216
217 148
        $url = Yii::getAlias($url);
218 148
        if ($url === '') {
219 9
            $url = Yii::$app->getRequest()->getUrl();
0 ignored issues
show
The method getUrl() does not exist on yii\console\Request. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
            $url = Yii::$app->getRequest()->/** @scrutinizer ignore-call */ getUrl();
Loading history...
220
        }
221
222 148
        if ($scheme === false) {
223 148
            return $url;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $url could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
224
        }
225
226 1
        if (static::isRelative($url)) {
0 ignored issues
show
It seems like $url can also be of type false; however, parameter $url of yii\helpers\BaseUrl::isRelative() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

226
        if (static::isRelative(/** @scrutinizer ignore-type */ $url)) {
Loading history...
227
            // turn relative URL into absolute
228 1
            $url = static::getUrlManager()->getHostInfo() . '/' . ltrim($url, '/');
0 ignored issues
show
It seems like $url can also be of type false; however, parameter $string of ltrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

228
            $url = static::getUrlManager()->getHostInfo() . '/' . ltrim(/** @scrutinizer ignore-type */ $url, '/');
Loading history...
229
        }
230
231 1
        return static::ensureScheme($url, $scheme);
0 ignored issues
show
It seems like $scheme can also be of type true; however, parameter $scheme of yii\helpers\BaseUrl::ensureScheme() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
        return static::ensureScheme($url, /** @scrutinizer ignore-type */ $scheme);
Loading history...
It seems like $url can also be of type false; however, parameter $url of yii\helpers\BaseUrl::ensureScheme() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
        return static::ensureScheme(/** @scrutinizer ignore-type */ $url, $scheme);
Loading history...
232
    }
233
234
    /**
235
     * Normalize the URL by ensuring it uses specified scheme.
236
     *
237
     * If the URL is relative or the scheme is not a string, normalization is skipped.
238
     *
239
     * @param string $url the URL to process
240
     * @param string $scheme the URI scheme used in the URL (e.g. `http` or `https`). Use an empty string to
241
     * create protocol-relative URL (e.g. `//example.com/path`)
242
     * @return string the processed URL
243
     * @since 2.0.11
244
     */
245 70
    public static function ensureScheme($url, $scheme)
246
    {
247 70
        if (static::isRelative($url) || !is_string($scheme)) {
248 61
            return $url;
249
        }
250
251 26
        if (strncmp($url, '//', 2) === 0) {
252
            // e.g. //example.com/path/to/resource
253 4
            return $scheme === '' ? $url : "$scheme:$url";
254
        }
255
256 24
        if (($pos = strpos($url, '://')) !== false) {
257 24
            if ($scheme === '') {
258 18
                $url = substr($url, $pos + 1);
259
            } else {
260 23
                $url = $scheme . substr($url, $pos);
261
            }
262
        }
263
264 24
        return $url;
265
    }
266
267
    /**
268
     * Returns the base URL of the current request.
269
     * @param bool|string $scheme the URI scheme to use in the returned base URL:
270
     *
271
     * - `false` (default): returning the base URL without host info.
272
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
273
     * - string: returning an absolute base URL with the specified scheme (either `http`, `https` or empty string
274
     *   for protocol-relative URL).
275
     * @return string
276
     */
277 1
    public static function base($scheme = false)
278
    {
279 1
        $url = static::getUrlManager()->getBaseUrl();
280 1
        if ($scheme !== false) {
281 1
            $url = static::getUrlManager()->getHostInfo() . $url;
282 1
            $url = static::ensureScheme($url, $scheme);
0 ignored issues
show
It seems like $scheme can also be of type true; however, parameter $scheme of yii\helpers\BaseUrl::ensureScheme() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

282
            $url = static::ensureScheme($url, /** @scrutinizer ignore-type */ $scheme);
Loading history...
283
        }
284
285 1
        return $url;
286
    }
287
288
    /**
289
     * Remembers the specified URL so that it can be later fetched back by [[previous()]].
290
     *
291
     * @param string|array $url the URL to remember. Please refer to [[to()]] for acceptable formats.
292
     * If this parameter is not specified, the currently requested URL will be used.
293
     * @param string|null $name the name associated with the URL to be remembered. This can be used
294
     * later by [[previous()]]. If not set, [[\yii\web\User::setReturnUrl()]] will be used with passed URL.
295
     * @see previous()
296
     * @see \yii\web\User::setReturnUrl()
297
     */
298 1
    public static function remember($url = '', $name = null)
299
    {
300 1
        $url = static::to($url);
301
302 1
        if ($name === null) {
303 1
            Yii::$app->getUser()->setReturnUrl($url);
0 ignored issues
show
The method getUser() does not exist on yii\console\Application. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

303
            Yii::$app->/** @scrutinizer ignore-call */ 
304
                       getUser()->setReturnUrl($url);
Loading history...
304
        } else {
305 1
            Yii::$app->getSession()->set($name, $url);
0 ignored issues
show
The method getSession() does not exist on yii\console\Application. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

305
            Yii::$app->/** @scrutinizer ignore-call */ 
306
                       getSession()->set($name, $url);
Loading history...
306
        }
307
    }
308
309
    /**
310
     * Returns the URL previously [[remember()|remembered]].
311
     *
312
     * @param string|null $name the named associated with the URL that was remembered previously.
313
     * If not set, [[\yii\web\User::getReturnUrl()]] will be used to obtain remembered URL.
314
     * @return string|null the URL previously remembered. Null is returned if no URL was remembered with the given name
315
     * and `$name` is not specified.
316
     * @see remember()
317
     * @see \yii\web\User::getReturnUrl()
318
     */
319 1
    public static function previous($name = null)
320
    {
321 1
        if ($name === null) {
322 1
            return Yii::$app->getUser()->getReturnUrl();
323
        }
324
325 1
        return Yii::$app->getSession()->get($name);
326
    }
327
328
    /**
329
     * Returns the canonical URL of the currently requested page.
330
     *
331
     * The canonical URL is constructed using the current controller's [[\yii\web\Controller::route]] and
332
     * [[\yii\web\Controller::actionParams]]. You may use the following code in the layout view to add a link tag
333
     * about canonical URL:
334
     *
335
     * ```php
336
     * $this->registerLinkTag(['rel' => 'canonical', 'href' => Url::canonical()]);
337
     * ```
338
     *
339
     * @return string the canonical URL of the currently requested page
340
     */
341 1
    public static function canonical()
342
    {
343 1
        $params = Yii::$app->controller->actionParams;
0 ignored issues
show
Bug Best Practice introduced by
The property actionParams does not exist on yii\console\Controller. Since you implemented __get, consider adding a @property annotation.
Loading history...
344 1
        $params[0] = Yii::$app->controller->getRoute();
345
346 1
        return static::getUrlManager()->createAbsoluteUrl($params);
347
    }
348
349
    /**
350
     * Returns the home URL.
351
     *
352
     * @param bool|string $scheme the URI scheme to use for the returned URL:
353
     *
354
     * - `false` (default): returning a relative URL.
355
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
356
     * - string: returning an absolute URL with the specified scheme (either `http`, `https` or empty string
357
     *   for protocol-relative URL).
358
     *
359
     * @return string home URL
360
     */
361 1
    public static function home($scheme = false)
362
    {
363 1
        $url = Yii::$app->getHomeUrl();
0 ignored issues
show
The method getHomeUrl() does not exist on yii\console\Application. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

363
        /** @scrutinizer ignore-call */ 
364
        $url = Yii::$app->getHomeUrl();
Loading history...
364
365 1
        if ($scheme !== false) {
366 1
            $url = static::getUrlManager()->getHostInfo() . $url;
367 1
            $url = static::ensureScheme($url, $scheme);
0 ignored issues
show
It seems like $scheme can also be of type true; however, parameter $scheme of yii\helpers\BaseUrl::ensureScheme() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

367
            $url = static::ensureScheme($url, /** @scrutinizer ignore-type */ $scheme);
Loading history...
368
        }
369
370 1
        return $url;
371
    }
372
373
    /**
374
     * Returns a value indicating whether a URL is relative.
375
     * A relative URL does not have host info part.
376
     * @param string $url the URL to be checked
377
     * @return bool whether the URL is relative
378
     */
379 123
    public static function isRelative($url)
380
    {
381 123
        return preg_match('~^[[:alpha:]][[:alnum:]+-.]*://|^//~', $url) === 0;
382
    }
383
384
    /**
385
     * Creates a URL by using the current route and the GET parameters.
386
     *
387
     * You may modify or remove some of the GET parameters, or add additional query parameters through
388
     * the `$params` parameter. In particular, if you specify a parameter to be null, then this parameter
389
     * will be removed from the existing GET parameters; all other parameters specified in `$params` will
390
     * be merged with the existing GET parameters. For example,
391
     *
392
     * ```php
393
     * // assume $_GET = ['id' => 123, 'src' => 'google'], current route is "post/view"
394
     *
395
     * // /index.php?r=post%2Fview&id=123&src=google
396
     * echo Url::current();
397
     *
398
     * // /index.php?r=post%2Fview&id=123
399
     * echo Url::current(['src' => null]);
400
     *
401
     * // /index.php?r=post%2Fview&id=100&src=google
402
     * echo Url::current(['id' => 100]);
403
     * ```
404
     *
405
     * Note that if you're replacing array parameters with `[]` at the end you should specify `$params` as nested arrays.
406
     * For a `PostSearchForm` model where parameter names are `PostSearchForm[id]` and `PostSearchForm[src]` the syntax
407
     * would be the following:
408
     *
409
     * ```php
410
     * // index.php?r=post%2Findex&PostSearchForm%5Bid%5D=100&PostSearchForm%5Bsrc%5D=google
411
     * echo Url::current([
412
     *     $postSearch->formName() => ['id' => 100, 'src' => 'google'],
413
     * ]);
414
     * ```
415
     *
416
     * @param array $params an associative array of parameters that will be merged with the current GET parameters.
417
     * If a parameter value is null, the corresponding GET parameter will be removed.
418
     * @param bool|string $scheme the URI scheme to use in the generated URL:
419
     *
420
     * - `false` (default): generating a relative URL.
421
     * - `true`: returning an absolute base URL whose scheme is the same as that in [[\yii\web\UrlManager::$hostInfo]].
422
     * - string: generating an absolute URL with the specified scheme (either `http`, `https` or empty string
423
     *   for protocol-relative URL).
424
     *
425
     * @return string the generated URL
426
     * @since 2.0.3
427
     */
428 1
    public static function current(array $params = [], $scheme = false)
429
    {
430 1
        $currentParams = Yii::$app->getRequest()->getQueryParams();
0 ignored issues
show
The method getQueryParams() does not exist on yii\console\Request. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

430
        $currentParams = Yii::$app->getRequest()->/** @scrutinizer ignore-call */ getQueryParams();
Loading history...
431 1
        $currentParams[0] = '/' . Yii::$app->controller->getRoute();
432 1
        $route = array_replace_recursive($currentParams, $params);
433 1
        return static::toRoute($route, $scheme);
434
    }
435
436
    /**
437
     * @return \yii\web\UrlManager URL manager used to create URLs
438
     * @since 2.0.8
439
     */
440 19
    protected static function getUrlManager()
441
    {
442 19
        return static::$urlManager ?: Yii::$app->getUrlManager();
443
    }
444
}
445