Passed
Push — v3 ( 98589c...9c2e98 )
by Andrew
26:31 queued 19:48
created

Redirects::findRedirectMatch()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 23
c 3
b 0
f 0
dl 0
loc 38
ccs 0
cts 23
cp 0
rs 8.6186
cc 7
nc 16
nop 3
crap 56
1
<?php
2
/**
3
 * Retour plugin for Craft CMS 3.x
4
 *
5
 * Retour allows you to intelligently redirect legacy URLs, so that you don't
6
 * lose SEO value when rebuilding & restructuring a website
7
 *
8
 * @link      https://nystudio107.com/
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) 2018 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\retour\services;
13
14
use nystudio107\retour\Retour;
15
use nystudio107\retour\events\RedirectEvent;
16
use nystudio107\retour\events\ResolveRedirectEvent;
17
use nystudio107\retour\events\RedirectResolvedEvent;
18
use nystudio107\retour\helpers\UrlHelper;
19
use nystudio107\retour\models\StaticRedirects as StaticRedirectsModel;
20
21
use Craft;
22
use craft\base\Component;
23
use craft\base\Plugin;
24
use craft\db\Query;
25
use craft\errors\SiteNotFoundException;
26
use craft\helpers\Db;
27
28
use yii\base\ExitException;
29
use yii\base\InvalidConfigException;
30
use yii\base\InvalidRouteException;
31
use yii\caching\TagDependency;
32
use yii\db\Exception;
33
34
/** @noinspection MissingPropertyAnnotationsInspection */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
35
36
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
37
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
38
 * @package   Retour
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
39
 * @since     3.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
40
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
41
class Redirects extends Component
42
{
43
    // Constants
44
    // =========================================================================
45
46
    const CACHE_KEY = 'retour_redirect_';
47
48
    const GLOBAL_REDIRECTS_CACHE_TAG = 'retour_redirects';
49
50
    const EVENT_REDIRECT_ID = 0;
51
52
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
53
     * @event RedirectEvent The event that is triggered before the redirect is saved
54
     * You may set [[RedirectEvent::isValid]] to `false` to prevent the redirect from getting saved.
55
     *
56
     * ```php
57
     * use nystudio107\retour\services\Redirects;
58
     * use nystudio107\retour\events\RedirectEvent;
59
     *
60
     * Event::on(Redirects::class,
61
     *     Redirects::EVENT_BEFORE_SAVE_REDIRECT,
62
     *     function(RedirectEvent $event) {
63
     *         // potentially set $event->isValid;
64
     *     }
65
     * );
66
     * ```
67
     */
68
    const EVENT_BEFORE_SAVE_REDIRECT = 'beforeSaveRedirect';
69
70
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
71
     * @event RedirectEvent The event that is triggered after the redirect is saved
72
     *
73
     * ```php
74
     * use nystudio107\retour\services\Redirects;
75
     * use nystudio107\retour\events\RedirectEvent;
76
     *
77
     * Event::on(Redirects::class,
78
     *     Redirects::EVENT_AFTER_SAVE_REDIRECT,
79
     *     function(RedirectEvent $event) {
80
     *         // the redirect was saved
81
     *     }
82
     * );
83
     * ```
84
     */
85
    const EVENT_AFTER_SAVE_REDIRECT = 'afterSaveRedirect';
86
87
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
88
     * @event ResolveRedirectEvent The event that is triggered before Retour has attempted
89
     *        to resolve redirects. You may set [[ResolveRedirectEvent::redirectDestUrl]] to
90
     *        to the URL that it should redirect to, or null if no redirect should happen
91
     *
92
     * ```php
93
     * use nystudio107\retour\services\Redirects;
94
     * use nystudio107\retour\events\ResolveRedirectEvent;
95
     *
96
     * Event::on(Redirects::class,
97
     *     Redirects::EVENT_AFTER_SAVE_REDIRECT,
98
     *     function(ResolveRedirectEvent $event) {
99
     *         // potentially set $event->redirectDestUrl;
100
     *     }
101
     * );
102
     * ```
103
     */
104
    const EVENT_BEFORE_RESOLVE_REDIRECT = 'beforeResolveRedirect';
105
106
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
107
     * @event ResolveRedirectEvent The event that is triggered after Retour has attempted
108
     *        to resolve redirects. You may set [[ResolveRedirectEvent::redirectDestUrl]] to
109
     *        to the URL that it should redirect to, or null if no redirect should happen
110
     *
111
     * ```php
112
     * use nystudio107\retour\services\Redirects;
113
     * use nystudio107\retour\events\ResolveRedirectEvent;
114
     *
115
     * Event::on(Redirects::class,
116
     *     Redirects::EVENT_AFTER_RESOLVE_REDIRECT,
117
     *     function(ResolveRedirectEvent $event) {
118
     *         // potentially set $event->redirectDestUrl;
119
     *     }
120
     * );
121
     * ```
122
     */
123
    const EVENT_AFTER_RESOLVE_REDIRECT = 'afterResolveRedirect';
124
125
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
126
     * @event RedirectResolvedEvent The event that is triggered once Retour has resolved
127
     *        a redirect. [[RedirectResolvedEvent::redirect]] will be set to the redirect
128
     *        that was resolved. You may set [[RedirectResolvedEvent::redirectDestUrl]] to
129
     *        to a different URL that it should redirect to, or leave it null if the
130
     *        redirect should happen as resolved.
131
     *
132
     * ```php
133
     * use nystudio107\retour\services\Redirects;
134
     * use nystudio107\retour\events\RedirectResolvedEvent;
135
     *
136
     * Event::on(Redirects::class,
137
     *     Redirects::EVENT_REDIRECT_RESOLVED,
138
     *     function(RedirectResolvedEvent $event) {
139
     *         // potentially set $event->redirectDestUrl;
140
     *     }
141
     * );
142
     * ```
143
     */
144
    const EVENT_REDIRECT_RESOLVED = 'redirectResolved';
145
146
    // Protected Properties
147
    // =========================================================================
148
149
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
150
     * @var null|array
151
     */
152
    protected $cachedStaticRedirects;
153
154
    // Public Methods
155
    // =========================================================================
156
157
    /**
158
     * Handle 404s by looking for redirects
159
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
160
    public function handle404()
161
    {
162
        Craft::info(
163
            Craft::t(
164
                'retour',
165
                'A 404 exception occurred'
166
            ),
167
            __METHOD__
168
        );
169
        $request = Craft::$app->getRequest();
170
        // We only want site requests that are not live preview or console requests
171
        if ($request->getIsSiteRequest() && !$this->isPreview($request) && !$request->getIsConsoleRequest()) {
172
            // See if we should redirect
173
            try {
174
                $fullUrl = urldecode($request->getAbsoluteUrl());
175
                $pathOnly = urldecode($request->getUrl());
176
            } catch (InvalidConfigException $e) {
177
                Craft::error(
178
                    $e->getMessage(),
179
                    __METHOD__
180
                );
181
                $pathOnly = '';
182
                $fullUrl = '';
183
            }
184
            // Strip the query string if `alwaysStripQueryString` is set
185
            if (Retour::$settings->alwaysStripQueryString) {
186
                $fullUrl = UrlHelper::stripQueryString($fullUrl);
187
                $pathOnly = UrlHelper::stripQueryString($pathOnly);
188
            }
189
            Craft::info(
190
                Craft::t(
191
                    'retour',
192
                    '404 full URL: {fullUrl}, 404 path only: {pathOnly}',
193
                    ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly]
194
                ),
195
                __METHOD__
196
            );
197
            if (!$this->excludeUri($pathOnly)) {
198
                // Redirect if we find a match, otherwise let Craft handle it
199
                $redirect = $this->findRedirectMatch($fullUrl, $pathOnly);
200
                if (!$this->doRedirect($fullUrl, $pathOnly, $redirect) && !Retour::$settings->alwaysStripQueryString) {
201
                    // Try it again without the query string
202
                    $fullUrl = UrlHelper::stripQueryString($fullUrl);
203
                    $pathOnly = UrlHelper::stripQueryString($pathOnly);
204
                    $redirect = $this->findRedirectMatch($fullUrl, $pathOnly);
205
                    $this->doRedirect($fullUrl, $pathOnly, $redirect);
206
                }
207
                // Increment the stats
208
                Retour::$plugin->statistics->incrementStatistics($pathOnly, false);
209
            }
210
        }
211
    }
212
213
    /**
214
     * Do the redirect
215
     *
216
     * @param string     $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
217
     * @param string     $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
218
     * @param null|array $redirect
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
219
     *
220
     * @return bool false if not redirected
221
     */
222
    public function doRedirect(string $fullUrl, string $pathOnly, $redirect): bool
223
    {
224
        $response = Craft::$app->getResponse();
225
        if ($redirect !== null) {
226
            // Figure out what type of source matching was done
227
            $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly';
228
            switch ($redirectSrcMatch) {
229
                case 'pathonly':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
230
                    $url = $pathOnly;
231
                    break;
232
                case 'fullurl':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
233
                    $url = $fullUrl;
234
                    break;
235
                default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
236
                    $url = $pathOnly;
237
                    break;
238
            }
239
            $dest = $redirect['redirectDestUrl'];
240
            // If this isn't a full URL, make it one based on the appropriate site
241
            if (!UrlHelper::isFullUrl($dest)) {
242
                try {
243
                    $siteId = $redirect['siteId'] ?? null;
244
                    if ($siteId !== null) {
245
                        $siteId = (int)$siteId;
246
                    }
247
                    $dest = UrlHelper::siteUrl($dest, null, null, $siteId);
248
                } catch (\yii\base\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
249
                }
250
            }
251
            if (Retour::$settings->preserveQueryString) {
252
                $request = Craft::$app->getRequest();
253
                if (!empty($request->getQueryStringWithoutPath())) {
254
                    $dest .= '?' . $request->getQueryStringWithoutPath();
255
                }
256
            }
257
            $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound';
258
            // Parse reference tags for exact matches
259
            if ($redirectMatchType === 'exactmatch') {
260
                $dest = Craft::$app->elements->parseRefs($dest, $redirect['siteId'] ?? null);
261
            }
262
            $status = $redirect['redirectHttpCode'];
263
            Craft::info(
264
                Craft::t(
265
                    'retour',
266
                    'Redirecting {url} to {dest} with status {status}',
267
                    ['url' => $url, 'dest' => $dest, 'status' => $status]
268
                ),
269
                __METHOD__
270
            );
271
            // Increment the stats
272
            Retour::$plugin->statistics->incrementStatistics($url, true);
273
            // Handle a Retour return status > 400 to render the actual error template
274
            if ($status >= 400) {
275
                Retour::$currentException->statusCode = $status;
276
                $errorHandler = Craft::$app->getErrorHandler();
277
                $errorHandler->exception = Retour::$currentException;
278
                try {
279
                    $response = Craft::$app->runAction('templates/render-error');
280
                } catch (InvalidRouteException $e) {
281
                    Craft::error($e->getMessage(), __METHOD__);
282
                } catch (\yii\console\Exception $e) {
283
                    Craft::error($e->getMessage(), __METHOD__);
284
                }
285
            }
286
            // Sanitize the URL
287
            $dest = UrlHelper::sanitizeUrl($dest);
288
            // Add any additional headers (existing ones will be replaced)
289
            if (!empty(Retour::$settings->additionalHeaders)) {
290
                foreach (Retour::$settings->additionalHeaders as $additionalHeader) {
291
                    $response->headers->set($additionalHeader['name'], $additionalHeader['value']);
292
                }
293
            }
294
            // Redirect the request away;
295
            $response->redirect($dest, $status)->send();
296
            try {
297
                Craft::$app->end();
298
            } catch (ExitException $e) {
299
                Craft::error($e->getMessage(), __METHOD__);
300
            }
301
        }
302
303
        return false;
304
    }
305
306
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
307
     * @param string $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
308
     * @param string $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
309
     * @param null   $siteId
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $siteId is correct as it would always require null to be passed?
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
310
     *
311
     * @return array|null
312
     */
313
    public function findRedirectMatch(string $fullUrl, string $pathOnly, $siteId = null)
314
    {
315
        // Get the current site
316
        if ($siteId === null) {
0 ignored issues
show
introduced by
The condition $siteId === null is always true.
Loading history...
317
            $currentSite = Craft::$app->getSites()->currentSite;
318
            if ($currentSite) {
319
                $siteId = $currentSite->id;
320
            } else {
321
                $primarySite = Craft::$app->getSites()->primarySite;
322
                if ($currentSite) {
0 ignored issues
show
introduced by
$currentSite is of type null, thus it always evaluated to false.
Loading history...
323
                    $siteId = $primarySite->id;
324
                }
325
            }
326
        }
327
        // Try getting the full URL redirect from the cache
328
        $redirect = $this->getRedirectFromCache($fullUrl, $siteId);
0 ignored issues
show
Bug introduced by
It seems like $siteId can also be of type null; however, parameter $siteId of nystudio107\retour\servi...:getRedirectFromCache() does only seem to accept integer, 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

328
        $redirect = $this->getRedirectFromCache($fullUrl, /** @scrutinizer ignore-type */ $siteId);
Loading history...
329
        if ($redirect) {
330
            $this->incrementRedirectHitCount($redirect);
331
            $this->saveRedirectToCache($fullUrl, $redirect);
332
333
            return $redirect;
334
        }
335
        // Try getting the path only redirect from the cache
336
        $redirect = $this->getRedirectFromCache($pathOnly, $siteId);
337
        if ($redirect) {
338
            $this->incrementRedirectHitCount($redirect);
339
            $this->saveRedirectToCache($pathOnly, $redirect);
340
341
            return $redirect;
342
        }
343
        // Resolve static redirects
344
        $redirects = $this->getAllStaticRedirects(null, $siteId);
345
        $redirect = $this->resolveRedirect($fullUrl, $pathOnly, $redirects);
346
        if ($redirect) {
347
            return $redirect;
348
        }
349
350
        return null;
351
    }
352
353
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
354
     * @param          $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 10
Loading history...
355
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
356
     *
357
     * @return bool|array
358
     */
359
    public function getRedirectFromCache($url, int $siteId = 0)
360
    {
361
        $cache = Craft::$app->getCache();
362
        $cacheKey = $this::CACHE_KEY.md5($url).$siteId;
363
        $redirect = $cache->get($cacheKey);
364
        Craft::info(
365
            Craft::t(
366
                'retour',
367
                'Cached redirect hit for {url}',
368
                ['url' => $url]
369
            ),
370
            __METHOD__
371
        );
372
373
        return $redirect;
374
    }
375
376
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
377
     * @param  string $url
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 2
Loading history...
378
     * @param  array  $redirect
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 1 spaces but found 2
Loading history...
379
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
380
    public function saveRedirectToCache($url, $redirect)
381
    {
382
        $cache = Craft::$app->getCache();
383
        // Get the current site id
384
        $sites = Craft::$app->getSites();
385
        try {
386
            $siteId = $sites->getCurrentSite()->id;
387
        } catch (SiteNotFoundException $e) {
388
            $siteId = 1;
389
        }
390
        $cacheKey = $this::CACHE_KEY.md5($url).$siteId;
391
        // Create the dependency tags
392
        $dependency = new TagDependency([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
393
            'tags' => [
394
                $this::GLOBAL_REDIRECTS_CACHE_TAG,
395
                $this::GLOBAL_REDIRECTS_CACHE_TAG.$siteId,
396
            ],
397
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
398
        $cache->set($cacheKey, $redirect, Retour::$cacheDuration, $dependency);
399
        Craft::info(
400
            Craft::t(
401
                'retour',
402
                'Cached redirect saved for {url}',
403
                ['url' => $url]
404
            ),
405
            __METHOD__
406
        );
407
    }
408
409
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
410
     * @param string $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
411
     * @param string $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
412
     * @param array  $redirects
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
413
     *
414
     * @return array|null
415
     */
416
    public function resolveRedirect(string $fullUrl, string $pathOnly, array $redirects)
417
    {
418
        $result = null;
419
        // Throw the Redirects::EVENT_BEFORE_RESOLVE_REDIRECT event
420
        $event = new ResolveRedirectEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
421
            'fullUrl' => $fullUrl,
422
            'pathOnly' => $pathOnly,
423
            'redirectDestUrl' => null,
424
            'redirectHttpCode' => 301,
425
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
426
        $this->trigger(self::EVENT_BEFORE_RESOLVE_REDIRECT, $event);
427
        if ($event->redirectDestUrl !== null) {
428
            return $this->resolveEventRedirect($event);
429
        }
430
        // Iterate through the redirects
431
        foreach ($redirects as $redirect) {
432
            // Figure out what type of source matching to do
433
            $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly';
434
            $redirectEnabled = (bool)$redirect['enabled'];
435
            if ($redirectEnabled === true) {
436
                switch ($redirectSrcMatch) {
437
                    case 'pathonly':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
438
                        $url = $pathOnly;
439
                        break;
440
                    case 'fullurl':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
441
                        $url = $fullUrl;
442
                        break;
443
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
444
                        $url = $pathOnly;
445
                        break;
446
                }
447
                $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound';
448
                switch ($redirectMatchType) {
449
                    // Do a straight up match
450
                    case 'exactmatch':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
451
                        if (strcasecmp($redirect['redirectSrcUrlParsed'], $url) === 0) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
452
                            $this->incrementRedirectHitCount($redirect);
453
                            $this->saveRedirectToCache($url, $redirect);
454
455
                            // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
456
                            $event = new RedirectResolvedEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
457
                                'fullUrl' => $fullUrl,
458
                                'pathOnly' => $pathOnly,
459
                                'redirectDestUrl' => null,
460
                                'redirectHttpCode' => 301,
461
                                'redirect' => $redirect,
462
                            ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
463
                            $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
464
                            if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 28
Loading history...
465
                                return $this->resolveEventRedirect($event, $url, $redirect);
466
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 28
Loading history...
467
468
                            return $redirect;
469
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
470
                        break;
471
472
                    // Do a regex match
473
                    case 'regexmatch':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
474
                        $matchRegEx = '`'.$redirect['redirectSrcUrlParsed'].'`i';
475
                        try {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
476
                            if (preg_match($matchRegEx, $url) === 1) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
477
                                $this->incrementRedirectHitCount($redirect);
478
                                // If we're not associated with an EntryID, handle capture group replacement
479
                                if ((int)$redirect['associatedElementId'] === 0) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
480
                                    $redirect['redirectDestUrl'] = preg_replace(
481
                                        $matchRegEx,
482
                                        $redirect['redirectDestUrl'],
483
                                        $url
484
                                    );
485
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
486
                                $url = preg_replace('/([^:])(\/{2,})/', '$1/', $url);
487
                                $this->saveRedirectToCache($url, $redirect);
488
489
                                // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
490
                                $event = new RedirectResolvedEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
491
                                    'fullUrl' => $fullUrl,
492
                                    'pathOnly' => $pathOnly,
493
                                    'redirectDestUrl' => null,
494
                                    'redirectHttpCode' => 301,
495
                                    'redirect' => $redirect,
496
                                ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
497
                                $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
498
                                if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
499
                                    return $this->resolveEventRedirect($event, $url, $redirect);
500
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
501
502
                                return $redirect;
503
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
504
                        } catch (\Exception $e) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
505
                            // That's fine
506
                            Craft::error('Invalid Redirect Regex: '.$matchRegEx, __METHOD__);
507
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
508
509
                        break;
510
511
                    // Otherwise try to look up a plugin's method by and call it for the match
512
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
513
                        $plugin = $redirectMatchType ? Craft::$app->getPlugins()->getPlugin($redirectMatchType) : null;
514
                        if ($plugin && method_exists($plugin, 'retourMatch')) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
515
                            $args = [
516
                                [
517
                                    'redirect' => &$redirect,
518
                                ],
519
                            ];
520
                            $result = \call_user_func_array([$plugin, 'retourMatch'], $args);
521
                            if ($result) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
522
                                $this->incrementRedirectHitCount($redirect);
523
                                $this->saveRedirectToCache($url, $redirect);
524
525
                                // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
526
                                $event = new RedirectResolvedEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
527
                                    'fullUrl' => $fullUrl,
528
                                    'pathOnly' => $pathOnly,
529
                                    'redirectDestUrl' => null,
530
                                    'redirectHttpCode' => 301,
531
                                    'redirect' => $redirect,
532
                                ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
533
                                $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
534
                                if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
535
                                    return $this->resolveEventRedirect($event, $url, $redirect);
536
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
537
538
                                return $redirect;
539
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
540
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
541
                        break;
542
                }
543
            }
544
        }
545
        // Throw the Redirects::EVENT_AFTER_RESOLVE_REDIRECT event
546
        $event = new ResolveRedirectEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
547
            'fullUrl' => $fullUrl,
548
            'pathOnly' => $pathOnly,
549
            'redirectDestUrl' => null,
550
            'redirectHttpCode' => 301,
551
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
552
        $this->trigger(self::EVENT_AFTER_RESOLVE_REDIRECT, $event);
553
        if ($event->redirectDestUrl !== null) {
554
            return $this->resolveEventRedirect($event);
555
        }
556
        Craft::info(
557
            Craft::t(
558
                'retour',
559
                'Not handled-> full URL: {fullUrl}, path only: {pathOnly}',
560
                ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly]
561
            ),
562
            __METHOD__
563
        );
564
565
        return $result;
566
    }
567
568
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $url should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $redirect should have a doc-comment as per coding-style.
Loading history...
569
     * @param ResolveRedirectEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
570
     *
571
     * @return null|array
572
     */
573
    public function resolveEventRedirect(ResolveRedirectEvent $event, $url = null, $redirect = null)
574
    {
575
        $result = null;
576
577
        if ($event->redirectDestUrl !== null) {
578
            $resolvedRedirect = new StaticRedirectsModel([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
579
                'id' => self::EVENT_REDIRECT_ID,
580
                'redirectDestUrl' => $event->redirectDestUrl,
581
                'redirectHttpCode' => $event->redirectHttpCode,
582
            ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
583
            $result = $resolvedRedirect->toArray();
584
585
            if ($url !== null && $redirect !== null) {
586
                // Save the modified redirect to the cache
587
                $redirect['redirectDestUrl'] = $event->redirectDestUrl;
588
                $redirect['redirectHttpCode'] = $event->redirectHttpCode;
589
                $this->saveRedirectToCache($url, $redirect);
590
            }
591
        }
592
593
        return $result;
594
    }
595
596
    /**
597
     * Returns the list of matching schemes
598
     *
599
     * @return  array
0 ignored issues
show
Coding Style introduced by
Tag value for @return tag indented incorrectly; expected 1 spaces but found 2
Loading history...
600
     */
601
    public function getMatchesList(): array
602
    {
603
        $result = [
604
            'exactmatch' => Craft::t('retour', 'Exact Match'),
605
            'regexmatch' => Craft::t('retour', 'RegEx Match'),
606
        ];
607
608
        // Add any plugins that offer the retourMatch() method
609
        foreach (Craft::$app->getPlugins()->getAllPlugins() as $plugin) {
610
            /** @var Plugin $plugin */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
611
            if (method_exists($plugin, 'retourMatch')) {
612
                $result[$plugin->getHandle()] = $plugin->name.Craft::t('retour', ' Match');
613
            }
614
        }
615
616
        return $result;
617
    }
618
619
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
620
     * @param null|int $limit
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
621
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
622
     *
623
     * @return array All of the statistics
624
     */
625
    public function getAllStaticRedirects($limit = null, int $siteId = null): array
626
    {
627
        // Cache it in our class; no need to fetch it more than once
628
        if ($this->cachedStaticRedirects !== null) {
629
            return $this->cachedStaticRedirects;
630
        }
631
        // Query the db table
632
        $query = (new Query())
633
            ->from(['{{%retour_static_redirects}}'])
634
            ->orderBy('redirectMatchType ASC, redirectSrcMatch ASC, hitCount DESC');
635
        if ($siteId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $siteId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
636
            $query
637
                ->where(['siteId' => $siteId])
638
                ->orWhere(['siteId' => null]);
639
        }
640
        if ($limit) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $limit of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
641
            $query->limit($limit);
642
        }
643
        $redirects = $query->all();
644
        // Cache for future accesses
645
        $this->cachedStaticRedirects = $redirects;
646
647
        return $redirects;
648
    }
649
650
    /**
651
     * Return a redirect by id
652
     *
653
     * @param int $id
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
654
     *
655
     * @return null|array The static redirect
656
     */
657
    public function getRedirectById(int $id)
658
    {
659
        // Query the db table
660
        $redirect = (new Query())
661
            ->from(['{{%retour_static_redirects}}'])
662
            ->where(['id' => $id])
663
            ->one();
664
665
        return $redirect;
666
    }
667
668
    /**
669
     * Return a redirect by redirectSrcUrl
670
     *
671
     * @param string   $redirectSrcUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
672
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
673
     *
674
     * @return null|array
675
     */
676
    public function getRedirectByRedirectSrcUrl(string $redirectSrcUrl, int $siteId = null)
677
    {
678
        // Query the db table
679
        $query = (new Query())
680
            ->from(['{{%retour_static_redirects}}'])
681
            ->where(['redirectSrcUrl' => $redirectSrcUrl])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
682
            ;
683
        if ($siteId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $siteId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
684
            $query
685
                ->andWhere(['or', [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
686
                    'siteId' => $siteId,
687
                ], [
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 20 spaces, but found 16.
Loading history...
688
                    'siteId' => null,
689
                ]]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
690
        }
691
        $redirect = $query->one();
692
693
        return $redirect;
694
    }
695
696
    /**
697
     * Delete a redirect by id
698
     *
699
     * @param int $id
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
700
     *
701
     * @return int The result
702
     */
703
    public function deleteRedirectById(int $id): int
704
    {
705
        $db = Craft::$app->getDb();
706
        // Delete a row from the db table
707
        try {
708
            $result = $db->createCommand()->delete(
709
                '{{%retour_static_redirects}}',
710
                [
711
                    'id' => $id,
712
                ]
713
            )->execute();
714
        } catch (Exception $e) {
715
            Craft::error($e->getMessage(), __METHOD__);
716
            $result = 0;
717
        }
718
719
        return $result;
720
    }
721
722
    /**
723
     * Increment the retour_static_redirects record
724
     *
725
     * @param $redirectConfig
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
726
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
727
    public function incrementRedirectHitCount(&$redirectConfig)
728
    {
729
        if ($redirectConfig !== null) {
730
            $db = Craft::$app->getDb();
731
            $redirectConfig['hitCount']++;
732
            $redirectConfig['hitLastTime'] = Db::prepareDateForDb(new \DateTime());
733
            Craft::debug(
734
                Craft::t(
735
                    'retour',
736
                    'Incrementing statistics for: {redirect}',
737
                    ['redirect' => print_r($redirectConfig, true)]
738
                ),
739
                __METHOD__
740
            );
741
            // Update the existing record
742
            try {
743
                $rowsAffected = $db->createCommand()->update(
744
                    '{{%retour_static_redirects}}',
745
                    [
746
                        'hitCount' => $redirectConfig['hitCount'],
747
                        'hitLastTime' => $redirectConfig['hitLastTime'],
748
                    ],
749
                    [
750
                        'id' => $redirectConfig['id'],
751
                    ]
752
                )->execute();
753
                Craft::debug('Rows affected: '.$rowsAffected, __METHOD__);
754
            } catch (\Exception $e) {
755
                Craft::error($e->getMessage(), __METHOD__);
756
            }
757
        }
758
    }
759
760
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
761
     * @param array $redirectConfig
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
762
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
763
    public function saveRedirect(array $redirectConfig)
764
    {
765
        // Validate the model before saving it to the db
766
        $redirect = new StaticRedirectsModel($redirectConfig);
767
        if ($redirect->validate() === false) {
768
            Craft::error(
769
                Craft::t(
770
                    'retour',
771
                    'Error validating redirect {id}: {errors}',
772
                    ['id' => $redirect->id, 'errors' => print_r($redirect->getErrors(), true)]
773
                ),
774
                __METHOD__
775
            );
776
777
            return;
778
        }
779
        // Get the validated model attributes and save them to the db
780
        $redirectConfig = $redirect->getAttributes();
781
        // 0 for a siteId needs to be converted to null
782
        if (empty($redirectConfig['siteId']) || (int)$redirectConfig['siteId'] === 0) {
783
            $redirectConfig['siteId'] = null;
784
        }
785
        // Throw an event to before saving the redirect
786
        $db = Craft::$app->getDb();
787
        // See if a redirect exists with this source URL already
788
        if ((int)$redirectConfig['id'] === 0) {
789
            // Query the db table
790
            $redirect = (new Query())
791
                ->from(['{{%retour_static_redirects}}'])
792
                ->where(['redirectSrcUrlParsed' => $redirectConfig['redirectSrcUrlParsed']])
793
                ->andWhere(['siteId' => $redirectConfig['siteId']])
794
                ->one();
795
            // If it exists, update it rather than having duplicates
796
            if (!empty($redirect)) {
797
                $redirectConfig['id'] = $redirect['id'];
798
            }
799
        }
800
        // Trigger a 'beforeSaveRedirect' event
801
        $isNew = (int)$redirectConfig['id'] === 0;
802
        $event = new RedirectEvent([
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
803
            'isNew' => $isNew,
804
            'legacyUrl' => $redirectConfig['redirectSrcUrlParsed'],
805
            'destinationUrl' => $redirectConfig['redirectDestUrl'],
806
            'matchType' => $redirectConfig['redirectSrcMatch'],
807
            'redirectType' => $redirectConfig['redirectHttpCode'],
808
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
809
        $this->trigger(self::EVENT_BEFORE_SAVE_REDIRECT, $event);
810
        if (!$event->isValid) {
811
            return;
812
        }
813
        // See if this is an existing redirect
814
        if (!$isNew) {
815
            Craft::debug(
816
                Craft::t(
817
                    'retour',
818
                    'Updating existing redirect: {redirect}',
819
                    ['redirect' => print_r($redirectConfig, true)]
820
                ),
821
                __METHOD__
822
            );
823
            // Update the existing record
824
            try {
825
                $db->createCommand()->update(
826
                    '{{%retour_static_redirects}}',
827
                    $redirectConfig,
828
                    [
829
                        'id' => $redirectConfig['id'],
830
                    ]
831
                )->execute();
832
            } catch (Exception $e) {
833
                Craft::error($e->getMessage(), __METHOD__);
834
            }
835
        } else {
836
            Craft::debug(
837
                Craft::t(
838
                    'retour',
839
                    'Creating new redirect: {redirect}',
840
                    ['redirect' => print_r($redirectConfig, true)]
841
                ),
842
                __METHOD__
843
            );
844
            unset($redirectConfig['id']);
845
            // Create a new record
846
            try {
847
                $db->createCommand()->insert(
848
                    '{{%retour_static_redirects}}',
849
                    $redirectConfig
850
                )->execute();
851
            } catch (Exception $e) {
852
                Craft::error($e->getMessage(), __METHOD__);
853
            }
854
        }
855
        // To prevent redirect loops, see if any static redirects have our redirectDestUrl as their redirectSrcUrl
856
        $testRedirectConfig = $this->getRedirectByRedirectSrcUrl(
857
            $redirectConfig['redirectDestUrl'],
858
            $redirectConfig['siteId']
859
        );
860
        if ($testRedirectConfig !== null) {
861
            Craft::debug(
862
                Craft::t(
863
                    'retour',
864
                    'Deleting redirect to prevent a loop: {redirect}',
865
                    ['redirect' => print_r($testRedirectConfig, true)]
866
                ),
867
                __METHOD__
868
            );
869
            // Delete the redirect that has a redirectSrcUrl the same as this record's redirectDestUrl
870
            try {
871
                $db->createCommand()->delete(
872
                    '{{%retour_static_redirects}}',
873
                    ['id' => $testRedirectConfig['id']]
874
                )->execute();
875
            } catch (Exception $e) {
876
                Craft::error($e->getMessage(), __METHOD__);
877
            }
878
        }
879
        // Trigger a 'afterSaveRedirect' event
880
        $this->trigger(self::EVENT_AFTER_SAVE_REDIRECT, $event);
881
882
        // Invalidate caches after saving a redirect
883
        $this->invalidateCaches();
884
    }
885
886
    /**
887
     * Invalidate all of the redirects caches
888
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
889
    public function invalidateCaches()
890
    {
891
        $cache = Craft::$app->getCache();
892
        TagDependency::invalidate($cache, $this::GLOBAL_REDIRECTS_CACHE_TAG);
893
        // If they are using Craft 3.3 or later, clear the GraphQL caches too
894
        if (Retour::$craft33) {
895
            $gql = Craft::$app->getGql();
896
            if (method_exists($gql, 'invalidateCaches')) {
897
                $gql->invalidateCaches();
898
            }
899
        }
900
        Craft::info(
901
            Craft::t(
902
                'retour',
903
                'All redirect caches cleared'
904
            ),
905
            __METHOD__
906
        );
907
    }
908
909
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
910
     * @param $uri
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
911
     *
912
     * @return bool
913
     */
914
    public function excludeUri($uri): bool
915
    {
916
        $uri = '/'.ltrim($uri, '/');
917
        if (!empty(Retour::$settings->excludePatterns)) {
918
            foreach (Retour::$settings->excludePatterns as $excludePattern) {
919
                $pattern = '`'.$excludePattern['pattern'].'`i';
920
                try {
921
                    if (preg_match($pattern, $uri) === 1) {
922
                        return true;
923
                    }
924
                } catch (\Exception $e) {
925
                    // That's fine
926
                    Craft::error('Invalid exclude URI Regex: '.$pattern, __METHOD__);
927
                }
928
            }
929
        }
930
931
        return false;
932
    }
933
934
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $request should have a doc-comment as per coding-style.
Loading history...
935
     * Return whether this is a preview request of any kind
936
     *
937
     * @return bool
938
     */
939
    public function isPreview($request): bool
940
    {
941
        $isPreview = false;
942
        if (Retour::$craft32) {
943
            $isPreview = $request->getIsPreview();
944
        }
945
        $isLivePreview = $request->getIsLivePreview();
946
947
        return ($isPreview || $isLivePreview);
948
    }
949
}
950