Passed
Push — v3 ( 78dc30...ba665f )
by Andrew
25:48 queued 16:54
created

Redirects::saveRedirect()   D

Complexity

Conditions 14
Paths 158

Size

Total Lines 126
Code Lines 83

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 83
dl 0
loc 126
rs 4.9974
c 3
b 0
f 0
ccs 0
cts 78
cp 0
cc 14
nc 158
nop 1
crap 210

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            // Stash the $pathOnly for use when incrementing the statistics
190
            $originalPathOnly = $pathOnly;
191
            Craft::info(
192
                Craft::t(
193
                    'retour',
194
                    '404 full URL: {fullUrl}, 404 path only: {pathOnly}',
195
                    ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly]
196
                ),
197
                __METHOD__
198
            );
199
            if (!$this->excludeUri($pathOnly)) {
200
                // Redirect if we find a match, otherwise let Craft handle it
201
                $redirect = $this->findRedirectMatch($fullUrl, $pathOnly);
202
                if (!$this->doRedirect($fullUrl, $pathOnly, $redirect) && !Retour::$settings->alwaysStripQueryString) {
203
                    // Try it again without the query string
204
                    $fullUrl = UrlHelper::stripQueryString($fullUrl);
205
                    $pathOnly = UrlHelper::stripQueryString($pathOnly);
206
                    $redirect = $this->findRedirectMatch($fullUrl, $pathOnly);
207
                    $this->doRedirect($fullUrl, $pathOnly, $redirect);
208
                }
209
                // Increment the stats
210
                Retour::$plugin->statistics->incrementStatistics($originalPathOnly, false);
211
            }
212
        }
213
    }
214
215
    /**
216
     * Do the redirect
217
     *
218
     * @param string     $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
219
     * @param string     $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
220
     * @param null|array $redirect
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
221
     *
222
     * @return bool false if not redirected
223
     */
224
    public function doRedirect(string $fullUrl, string $pathOnly, $redirect): bool
225
    {
226
        $response = Craft::$app->getResponse();
227
        if ($redirect !== null) {
228
            // Figure out what type of source matching was done
229
            $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly';
230
            switch ($redirectSrcMatch) {
231
                case 'pathonly':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
232
                    $url = $pathOnly;
233
                    break;
234
                case 'fullurl':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
235
                    $url = $fullUrl;
236
                    break;
237
                default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 12 spaces, found 16
Loading history...
238
                    $url = $pathOnly;
239
                    break;
240
            }
241
            $dest = $redirect['redirectDestUrl'];
242
            // If this isn't a full URL, make it one based on the appropriate site
243
            if (!UrlHelper::isFullUrl($dest)) {
244
                try {
245
                    $siteId = $redirect['siteId'] ?? null;
246
                    if ($siteId !== null) {
247
                        $siteId = (int)$siteId;
248
                    }
249
                    $dest = UrlHelper::siteUrl($dest, null, null, $siteId);
250
                } 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...
251
                }
252
            }
253
            if (Retour::$settings->preserveQueryString) {
254
                $request = Craft::$app->getRequest();
255
                if (!empty($request->getQueryStringWithoutPath())) {
256
                    $dest .= '?' . $request->getQueryStringWithoutPath();
257
                }
258
            }
259
            $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound';
260
            // Parse reference tags for exact matches
261
            if ($redirectMatchType === 'exactmatch') {
262
                $dest = Craft::$app->elements->parseRefs($dest, $redirect['siteId'] ?? null);
0 ignored issues
show
Bug introduced by
The method parseRefs() does not exist on null. ( Ignorable by Annotation )

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

262
                /** @scrutinizer ignore-call */ 
263
                $dest = Craft::$app->elements->parseRefs($dest, $redirect['siteId'] ?? null);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
263
            }
264
            $status = $redirect['redirectHttpCode'];
265
            Craft::info(
266
                Craft::t(
267
                    'retour',
268
                    'Redirecting {url} to {dest} with status {status}',
269
                    ['url' => $url, 'dest' => $dest, 'status' => $status]
270
                ),
271
                __METHOD__
272
            );
273
            // Increment the stats
274
            Retour::$plugin->statistics->incrementStatistics($url, true);
275
            // Handle a Retour return status > 400 to render the actual error template
276
            if ($status >= 400) {
277
                Retour::$currentException->statusCode = $status;
278
                $errorHandler = Craft::$app->getErrorHandler();
279
                $errorHandler->exception = Retour::$currentException;
280
                try {
281
                    $response = Craft::$app->runAction('templates/render-error');
282
                } catch (InvalidRouteException $e) {
283
                    Craft::error($e->getMessage(), __METHOD__);
284
                } catch (\yii\console\Exception $e) {
285
                    Craft::error($e->getMessage(), __METHOD__);
286
                }
287
            }
288
            // Sanitize the URL
289
            $dest = UrlHelper::sanitizeUrl($dest);
290
            // Add any additional headers (existing ones will be replaced)
291
            if (!empty(Retour::$settings->additionalHeaders)) {
292
                foreach (Retour::$settings->additionalHeaders as $additionalHeader) {
293
                    $response->headers->set($additionalHeader['name'], $additionalHeader['value']);
294
                }
295
            }
296
            // Redirect the request away;
297
            $response->redirect($dest, $status)->send();
298
            try {
299
                Craft::$app->end();
300
            } catch (ExitException $e) {
301
                Craft::error($e->getMessage(), __METHOD__);
302
            }
303
        }
304
305
        return false;
306
    }
307
308
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
309
     * @param string $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
310
     * @param string $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
311
     * @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...
312
     *
313
     * @return array|null
314
     */
315
    public function findRedirectMatch(string $fullUrl, string $pathOnly, $siteId = null)
316
    {
317
        // Get the current site
318
        if ($siteId === null) {
0 ignored issues
show
introduced by
The condition $siteId === null is always true.
Loading history...
319
            $currentSite = Craft::$app->getSites()->currentSite;
320
            if ($currentSite) {
321
                $siteId = $currentSite->id;
322
            } else {
323
                $primarySite = Craft::$app->getSites()->primarySite;
324
                if ($currentSite) {
325
                    $siteId = $primarySite->id;
326
                }
327
            }
328
        }
329
        // Try getting the full URL redirect from the cache
330
        $redirect = $this->getRedirectFromCache($fullUrl, $siteId);
331
        if ($redirect) {
332
            $this->incrementRedirectHitCount($redirect);
333
            $this->saveRedirectToCache($fullUrl, $redirect);
334
335
            return $redirect;
336
        }
337
        // Try getting the path only redirect from the cache
338
        $redirect = $this->getRedirectFromCache($pathOnly, $siteId);
339
        if ($redirect) {
340
            $this->incrementRedirectHitCount($redirect);
341
            $this->saveRedirectToCache($pathOnly, $redirect);
342
343
            return $redirect;
344
        }
345
        // Resolve static redirects
346
        $redirects = $this->getAllStaticRedirects(null, $siteId);
347
        $redirect = $this->resolveRedirect($fullUrl, $pathOnly, $redirects);
348
        if ($redirect) {
349
            return $redirect;
350
        }
351
352
        return null;
353
    }
354
355
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
356
     * @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...
357
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
358
     *
359
     * @return bool|array
360
     */
361
    public function getRedirectFromCache($url, int $siteId = 0)
362
    {
363
        $cache = Craft::$app->getCache();
364
        $cacheKey = $this::CACHE_KEY.md5($url).$siteId;
365
        $redirect = $cache->get($cacheKey);
366
        Craft::info(
367
            Craft::t(
368
                'retour',
369
                'Cached redirect hit for {url}',
370
                ['url' => $url]
371
            ),
372
            __METHOD__
373
        );
374
375
        return $redirect;
376
    }
377
378
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
379
     * @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...
380
     * @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...
381
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
382
    public function saveRedirectToCache($url, $redirect)
383
    {
384
        $cache = Craft::$app->getCache();
385
        // Get the current site id
386
        $sites = Craft::$app->getSites();
387
        try {
388
            $siteId = $sites->getCurrentSite()->id;
389
        } catch (SiteNotFoundException $e) {
390
            $siteId = 1;
391
        }
392
        $cacheKey = $this::CACHE_KEY.md5($url).$siteId;
393
        // Create the dependency tags
394
        $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...
395
            'tags' => [
396
                $this::GLOBAL_REDIRECTS_CACHE_TAG,
397
                $this::GLOBAL_REDIRECTS_CACHE_TAG.$siteId,
398
            ],
399
        ]);
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...
400
        $cache->set($cacheKey, $redirect, Retour::$cacheDuration, $dependency);
401
        Craft::info(
402
            Craft::t(
403
                'retour',
404
                'Cached redirect saved for {url}',
405
                ['url' => $url]
406
            ),
407
            __METHOD__
408
        );
409
    }
410
411
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
412
     * @param string $fullUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
413
     * @param string $pathOnly
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
414
     * @param array  $redirects
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
415
     *
416
     * @return array|null
417
     */
418
    public function resolveRedirect(string $fullUrl, string $pathOnly, array $redirects)
419
    {
420
        $result = null;
421
        // Throw the Redirects::EVENT_BEFORE_RESOLVE_REDIRECT event
422
        $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...
423
            'fullUrl' => $fullUrl,
424
            'pathOnly' => $pathOnly,
425
            'redirectDestUrl' => null,
426
            'redirectHttpCode' => 301,
427
        ]);
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...
428
        $this->trigger(self::EVENT_BEFORE_RESOLVE_REDIRECT, $event);
429
        if ($event->redirectDestUrl !== null) {
430
            return $this->resolveEventRedirect($event);
431
        }
432
        // Iterate through the redirects
433
        foreach ($redirects as $redirect) {
434
            // Figure out what type of source matching to do
435
            $redirectSrcMatch = $redirect['redirectSrcMatch'] ?? 'pathonly';
436
            $redirectEnabled = (bool)$redirect['enabled'];
437
            if ($redirectEnabled === true) {
438
                switch ($redirectSrcMatch) {
439
                    case 'pathonly':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
440
                        $url = $pathOnly;
441
                        break;
442
                    case 'fullurl':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
443
                        $url = $fullUrl;
444
                        break;
445
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
446
                        $url = $pathOnly;
447
                        break;
448
                }
449
                $redirectMatchType = $redirect['redirectMatchType'] ?? 'notfound';
450
                switch ($redirectMatchType) {
451
                    // Do a straight up match
452
                    case 'exactmatch':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
453
                        if (strcasecmp($redirect['redirectSrcUrlParsed'], $url) === 0) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
454
                            $this->incrementRedirectHitCount($redirect);
455
                            $this->saveRedirectToCache($url, $redirect);
456
457
                            // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
458
                            $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...
459
                                'fullUrl' => $fullUrl,
460
                                'pathOnly' => $pathOnly,
461
                                'redirectDestUrl' => null,
462
                                'redirectHttpCode' => 301,
463
                                'redirect' => $redirect,
464
                            ]);
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...
465
                            $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
466
                            if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 28
Loading history...
467
                                return $this->resolveEventRedirect($event, $url, $redirect);
468
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 28
Loading history...
469
470
                            return $redirect;
471
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
472
                        break;
473
474
                    // Do a regex match
475
                    case 'regexmatch':
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
476
                        $matchRegEx = '`'.$redirect['redirectSrcUrlParsed'].'`i';
477
                        try {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
478
                            if (preg_match($matchRegEx, $url) === 1) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
479
                                $this->incrementRedirectHitCount($redirect);
480
                                // If we're not associated with an EntryID, handle capture group replacement
481
                                if ((int)$redirect['associatedElementId'] === 0) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
482
                                    $redirect['redirectDestUrl'] = preg_replace(
483
                                        $matchRegEx,
484
                                        $redirect['redirectDestUrl'],
485
                                        $url
486
                                    );
487
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 28 spaces, found 32
Loading history...
488
                                $url = preg_replace('/([^:])(\/{2,})/', '$1/', $url);
489
                                $this->saveRedirectToCache($url, $redirect);
490
491
                                // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
492
                                $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...
493
                                    'fullUrl' => $fullUrl,
494
                                    'pathOnly' => $pathOnly,
495
                                    'redirectDestUrl' => null,
496
                                    'redirectHttpCode' => 301,
497
                                    'redirect' => $redirect,
498
                                ]);
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...
499
                                $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
500
                                if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
501
                                    return $this->resolveEventRedirect($event, $url, $redirect);
502
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
503
504
                                return $redirect;
505
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
506
                        } catch (\Exception $e) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
507
                            // That's fine
508
                            Craft::error('Invalid Redirect Regex: '.$matchRegEx, __METHOD__);
509
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
510
511
                        break;
512
513
                    // Otherwise try to look up a plugin's method by and call it for the match
514
                    default:
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 16 spaces, found 20
Loading history...
515
                        $plugin = $redirectMatchType ? Craft::$app->getPlugins()->getPlugin($redirectMatchType) : null;
516
                        if ($plugin && method_exists($plugin, 'retourMatch')) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
517
                            $args = [
518
                                [
519
                                    'redirect' => &$redirect,
520
                                ],
521
                            ];
522
                            $result = \call_user_func_array([$plugin, 'retourMatch'], $args);
523
                            if ($result) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
524
                                $this->incrementRedirectHitCount($redirect);
525
                                $this->saveRedirectToCache($url, $redirect);
526
527
                                // Throw the Redirects::EVENT_REDIRECT_RESOLVED event
528
                                $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...
529
                                    'fullUrl' => $fullUrl,
530
                                    'pathOnly' => $pathOnly,
531
                                    'redirectDestUrl' => null,
532
                                    'redirectHttpCode' => 301,
533
                                    'redirect' => $redirect,
534
                                ]);
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...
535
                                $this->trigger(self::EVENT_REDIRECT_RESOLVED, $event);
536
                                if ($event->redirectDestUrl !== null) {
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
537
                                    return $this->resolveEventRedirect($event, $url, $redirect);
538
                                }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 32
Loading history...
539
540
                                return $redirect;
541
                            }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 24 spaces, found 28
Loading history...
542
                        }
0 ignored issues
show
Coding Style introduced by
Line indented incorrectly; expected 20 spaces, found 24
Loading history...
543
                        break;
544
                }
545
            }
546
        }
547
        // Throw the Redirects::EVENT_AFTER_RESOLVE_REDIRECT event
548
        $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...
549
            'fullUrl' => $fullUrl,
550
            'pathOnly' => $pathOnly,
551
            'redirectDestUrl' => null,
552
            'redirectHttpCode' => 301,
553
        ]);
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...
554
        $this->trigger(self::EVENT_AFTER_RESOLVE_REDIRECT, $event);
555
        if ($event->redirectDestUrl !== null) {
556
            return $this->resolveEventRedirect($event);
557
        }
558
        Craft::info(
559
            Craft::t(
560
                'retour',
561
                'Not handled-> full URL: {fullUrl}, path only: {pathOnly}',
562
                ['fullUrl' => $fullUrl, 'pathOnly' => $pathOnly]
563
            ),
564
            __METHOD__
565
        );
566
567
        return $result;
568
    }
569
570
    /**
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...
571
     * @param ResolveRedirectEvent $event
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
572
     *
573
     * @return null|array
574
     */
575
    public function resolveEventRedirect(ResolveRedirectEvent $event, $url = null, $redirect = null)
576
    {
577
        $result = null;
578
579
        if ($event->redirectDestUrl !== null) {
580
            $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...
581
                'id' => self::EVENT_REDIRECT_ID,
582
                'redirectDestUrl' => $event->redirectDestUrl,
583
                'redirectHttpCode' => $event->redirectHttpCode,
584
            ]);
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...
585
            $result = $resolvedRedirect->toArray();
586
587
            if ($url !== null && $redirect !== null) {
588
                // Save the modified redirect to the cache
589
                $redirect['redirectDestUrl'] = $event->redirectDestUrl;
590
                $redirect['redirectHttpCode'] = $event->redirectHttpCode;
591
                $this->saveRedirectToCache($url, $redirect);
592
            }
593
        }
594
595
        return $result;
596
    }
597
598
    /**
599
     * Returns the list of matching schemes
600
     *
601
     * @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...
602
     */
603
    public function getMatchesList(): array
604
    {
605
        $result = [
606
            'exactmatch' => Craft::t('retour', 'Exact Match'),
607
            'regexmatch' => Craft::t('retour', 'RegEx Match'),
608
        ];
609
610
        // Add any plugins that offer the retourMatch() method
611
        foreach (Craft::$app->getPlugins()->getAllPlugins() as $plugin) {
612
            /** @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...
613
            if (method_exists($plugin, 'retourMatch')) {
614
                $result[$plugin->getHandle()] = $plugin->name.Craft::t('retour', ' Match');
615
            }
616
        }
617
618
        return $result;
619
    }
620
621
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
622
     * @param null|int $limit
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
623
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
624
     *
625
     * @return array All of the statistics
626
     */
627
    public function getAllStaticRedirects($limit = null, int $siteId = null): array
628
    {
629
        // Cache it in our class; no need to fetch it more than once
630
        if ($this->cachedStaticRedirects !== null) {
631
            return $this->cachedStaticRedirects;
632
        }
633
        // Query the db table
634
        $query = (new Query())
635
            ->from(['{{%retour_static_redirects}}'])
636
            ->orderBy('redirectMatchType ASC, redirectSrcMatch ASC, hitCount DESC');
637
        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...
638
            $query
639
                ->where(['siteId' => $siteId])
640
                ->orWhere(['siteId' => null]);
641
        }
642
        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...
643
            $query->limit($limit);
644
        }
645
        $redirects = $query->all();
646
        // Cache for future accesses
647
        $this->cachedStaticRedirects = $redirects;
648
649
        return $redirects;
650
    }
651
652
    /**
653
     * Return a redirect by id
654
     *
655
     * @param int $id
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
656
     *
657
     * @return null|array The static redirect
658
     */
659
    public function getRedirectById(int $id)
660
    {
661
        // Query the db table
662
        $redirect = (new Query())
663
            ->from(['{{%retour_static_redirects}}'])
664
            ->where(['id' => $id])
665
            ->one();
666
667
        return $redirect;
668
    }
669
670
    /**
671
     * Return a redirect by redirectSrcUrl
672
     *
673
     * @param string   $redirectSrcUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
674
     * @param int|null $siteId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
675
     *
676
     * @return null|array
677
     */
678
    public function getRedirectByRedirectSrcUrl(string $redirectSrcUrl, int $siteId = null)
679
    {
680
        // Query the db table
681
        $query = (new Query())
682
            ->from(['{{%retour_static_redirects}}'])
683
            ->where(['redirectSrcUrl' => $redirectSrcUrl])
0 ignored issues
show
Coding Style introduced by
Space after closing parenthesis of function call prohibited
Loading history...
684
            ;
685
        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...
686
            $query
687
                ->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...
688
                    'siteId' => $siteId,
689
                ], [
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...
690
                    'siteId' => null,
691
                ]]);
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...
692
        }
693
        $redirect = $query->one();
694
695
        return $redirect;
696
    }
697
698
    /**
699
     * Delete a redirect by id
700
     *
701
     * @param int $id
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
702
     *
703
     * @return int The result
704
     */
705
    public function deleteRedirectById(int $id): int
706
    {
707
        $db = Craft::$app->getDb();
708
        // Delete a row from the db table
709
        try {
710
            $result = $db->createCommand()->delete(
711
                '{{%retour_static_redirects}}',
712
                [
713
                    'id' => $id,
714
                ]
715
            )->execute();
716
        } catch (Exception $e) {
717
            Craft::error($e->getMessage(), __METHOD__);
718
            $result = 0;
719
        }
720
721
        return $result;
722
    }
723
724
    /**
725
     * Increment the retour_static_redirects record
726
     *
727
     * @param $redirectConfig
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
728
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
729
    public function incrementRedirectHitCount(&$redirectConfig)
730
    {
731
        if ($redirectConfig !== null) {
732
            $db = Craft::$app->getDb();
733
            $redirectConfig['hitCount']++;
734
            $redirectConfig['hitLastTime'] = Db::prepareDateForDb(new \DateTime());
735
            Craft::debug(
736
                Craft::t(
737
                    'retour',
738
                    'Incrementing statistics for: {redirect}',
739
                    ['redirect' => print_r($redirectConfig, true)]
740
                ),
741
                __METHOD__
742
            );
743
            // Update the existing record
744
            try {
745
                $rowsAffected = $db->createCommand()->update(
746
                    '{{%retour_static_redirects}}',
747
                    [
748
                        'hitCount' => $redirectConfig['hitCount'],
749
                        'hitLastTime' => $redirectConfig['hitLastTime'],
750
                    ],
751
                    [
752
                        'id' => $redirectConfig['id'],
753
                    ]
754
                )->execute();
755
                Craft::debug('Rows affected: '.$rowsAffected, __METHOD__);
756
            } catch (\Exception $e) {
757
                Craft::error($e->getMessage(), __METHOD__);
758
            }
759
        }
760
    }
761
762
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
763
     * @param array $redirectConfig
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
764
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
765
    public function saveRedirect(array $redirectConfig)
766
    {
767
        // Handle URL encoded URLs by decoding them before saving them
768
        if (isset($redirectConfig['redirectMatchType']) && $redirectConfig['redirectMatchType'] === 'exactmatch') {
769
            $redirectConfig['redirectSrcUrl'] = urldecode($redirectConfig['redirectSrcUrl'] ?? '');
770
            $redirectConfig['redirectSrcUrlParsed'] = urldecode($redirectConfig['redirectSrcUrlParsed'] ?? '');
771
        }
772
        // Validate the model before saving it to the db
773
        $redirect = new StaticRedirectsModel($redirectConfig);
774
        if ($redirect->validate() === false) {
775
            Craft::error(
776
                Craft::t(
777
                    'retour',
778
                    'Error validating redirect {id}: {errors}',
779
                    ['id' => $redirect->id, 'errors' => print_r($redirect->getErrors(), true)]
780
                ),
781
                __METHOD__
782
            );
783
784
            return;
785
        }
786
        // Get the validated model attributes and save them to the db
787
        $redirectConfig = $redirect->getAttributes();
788
        // 0 for a siteId needs to be converted to null
789
        if (empty($redirectConfig['siteId']) || (int)$redirectConfig['siteId'] === 0) {
790
            $redirectConfig['siteId'] = null;
791
        }
792
        // Throw an event to before saving the redirect
793
        $db = Craft::$app->getDb();
794
        // See if a redirect exists with this source URL already
795
        if ((int)$redirectConfig['id'] === 0) {
796
            // Query the db table
797
            $redirect = (new Query())
798
                ->from(['{{%retour_static_redirects}}'])
799
                ->where(['redirectSrcUrlParsed' => $redirectConfig['redirectSrcUrlParsed']])
800
                ->andWhere(['siteId' => $redirectConfig['siteId']])
801
                ->one();
802
            // If it exists, update it rather than having duplicates
803
            if (!empty($redirect)) {
804
                $redirectConfig['id'] = $redirect['id'];
805
            }
806
        }
807
        // Trigger a 'beforeSaveRedirect' event
808
        $isNew = (int)$redirectConfig['id'] === 0;
809
        $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...
810
            'isNew' => $isNew,
811
            'legacyUrl' => $redirectConfig['redirectSrcUrlParsed'],
812
            'destinationUrl' => $redirectConfig['redirectDestUrl'],
813
            'matchType' => $redirectConfig['redirectSrcMatch'],
814
            'redirectType' => $redirectConfig['redirectHttpCode'],
815
        ]);
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...
816
        $this->trigger(self::EVENT_BEFORE_SAVE_REDIRECT, $event);
817
        if (!$event->isValid) {
818
            return;
819
        }
820
        // See if this is an existing redirect
821
        if (!$isNew) {
822
            Craft::debug(
823
                Craft::t(
824
                    'retour',
825
                    'Updating existing redirect: {redirect}',
826
                    ['redirect' => print_r($redirectConfig, true)]
827
                ),
828
                __METHOD__
829
            );
830
            // Update the existing record
831
            try {
832
                $db->createCommand()->update(
833
                    '{{%retour_static_redirects}}',
834
                    $redirectConfig,
835
                    [
836
                        'id' => $redirectConfig['id'],
837
                    ]
838
                )->execute();
839
            } catch (Exception $e) {
840
                Craft::error($e->getMessage(), __METHOD__);
841
            }
842
        } else {
843
            Craft::debug(
844
                Craft::t(
845
                    'retour',
846
                    'Creating new redirect: {redirect}',
847
                    ['redirect' => print_r($redirectConfig, true)]
848
                ),
849
                __METHOD__
850
            );
851
            unset($redirectConfig['id']);
852
            // Create a new record
853
            try {
854
                $db->createCommand()->insert(
855
                    '{{%retour_static_redirects}}',
856
                    $redirectConfig
857
                )->execute();
858
            } catch (Exception $e) {
859
                Craft::error($e->getMessage(), __METHOD__);
860
            }
861
        }
862
        // To prevent redirect loops, see if any static redirects have our redirectDestUrl as their redirectSrcUrl
863
        $testRedirectConfig = $this->getRedirectByRedirectSrcUrl(
864
            $redirectConfig['redirectDestUrl'],
865
            $redirectConfig['siteId']
866
        );
867
        if ($testRedirectConfig !== null) {
868
            Craft::debug(
869
                Craft::t(
870
                    'retour',
871
                    'Deleting redirect to prevent a loop: {redirect}',
872
                    ['redirect' => print_r($testRedirectConfig, true)]
873
                ),
874
                __METHOD__
875
            );
876
            // Delete the redirect that has a redirectSrcUrl the same as this record's redirectDestUrl
877
            try {
878
                $db->createCommand()->delete(
879
                    '{{%retour_static_redirects}}',
880
                    ['id' => $testRedirectConfig['id']]
881
                )->execute();
882
            } catch (Exception $e) {
883
                Craft::error($e->getMessage(), __METHOD__);
884
            }
885
        }
886
        // Trigger a 'afterSaveRedirect' event
887
        $this->trigger(self::EVENT_AFTER_SAVE_REDIRECT, $event);
888
889
        // Invalidate caches after saving a redirect
890
        $this->invalidateCaches();
891
    }
892
893
    /**
894
     * Invalidate all of the redirects caches
895
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
896
    public function invalidateCaches()
897
    {
898
        $cache = Craft::$app->getCache();
899
        TagDependency::invalidate($cache, $this::GLOBAL_REDIRECTS_CACHE_TAG);
900
        // If they are using Craft 3.3 or later, clear the GraphQL caches too
901
        if (Retour::$craft33) {
902
            $gql = Craft::$app->getGql();
903
            if (method_exists($gql, 'invalidateCaches')) {
904
                $gql->invalidateCaches();
905
            }
906
        }
907
        Craft::info(
908
            Craft::t(
909
                'retour',
910
                'All redirect caches cleared'
911
            ),
912
            __METHOD__
913
        );
914
    }
915
916
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
917
     * @param $uri
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
918
     *
919
     * @return bool
920
     */
921
    public function excludeUri($uri): bool
922
    {
923
        $uri = '/'.ltrim($uri, '/');
924
        if (!empty(Retour::$settings->excludePatterns)) {
925
            foreach (Retour::$settings->excludePatterns as $excludePattern) {
926
                $pattern = '`'.$excludePattern['pattern'].'`i';
927
                try {
928
                    if (preg_match($pattern, $uri) === 1) {
929
                        return true;
930
                    }
931
                } catch (\Exception $e) {
932
                    // That's fine
933
                    Craft::error('Invalid exclude URI Regex: '.$pattern, __METHOD__);
934
                }
935
            }
936
        }
937
938
        return false;
939
    }
940
941
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $request should have a doc-comment as per coding-style.
Loading history...
942
     * Return whether this is a preview request of any kind
943
     *
944
     * @return bool
945
     */
946
    public function isPreview($request): bool
947
    {
948
        $isPreview = false;
949
        if (Retour::$craft32) {
950
            $isPreview = $request->getIsPreview();
951
        }
952
        $isLivePreview = $request->getIsLivePreview();
953
954
        return ($isPreview || $isLivePreview);
955
    }
956
}
957