Passed
Push — v4 ( 81628e...7020b0 )
by Andrew
43:05 queued 19:01
created

Sitemap::assetSitemapItem()   C

Complexity

Conditions 13
Paths 8

Size

Total Lines 39
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 30
c 1
b 1
f 0
dl 0
loc 39
ccs 0
cts 31
cp 0
rs 6.6166
cc 13
nc 8
nop 3
crap 182

How to fix   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
namespace nystudio107\seomatic\helpers;
4
5
use benf\neo\elements\Block as NeoBlock;
6
use Craft;
7
use craft\base\Element;
8
use craft\base\Event;
9
use craft\console\Application as ConsoleApplication;
10
use craft\db\Paginator;
11
use craft\elements\Asset;
12
use craft\elements\MatrixBlock;
13
use craft\errors\SiteNotFoundException;
14
use craft\fields\Assets as AssetsField;
15
use craft\queue\Queue;
16
use DateTime;
17
use nystudio107\fastcgicachebust\FastcgiCacheBust;
18
use nystudio107\seomatic\base\SeoElementInterface;
19
use nystudio107\seomatic\events\IncludeSitemapEntryEvent;
20
use nystudio107\seomatic\fields\SeoSettings;
21
use nystudio107\seomatic\helpers\Field as FieldHelper;
22
use nystudio107\seomatic\jobs\GenerateSitemap;
23
use nystudio107\seomatic\models\MetaBundle;
24
use nystudio107\seomatic\models\SitemapTemplate;
25
use nystudio107\seomatic\Seomatic;
26
use Throwable;
27
use verbb\supertable\elements\SuperTableBlockElement as SuperTableBlock;
28
use yii\base\Exception;
29
use yii\caching\TagDependency;
30
use yii\helpers\Html;
31
use function array_intersect_key;
32
use function count;
33
use function in_array;
34
35
/**
36
 * @author    nystudio107
37
 * @package   Seomatic
38
 * @since     3.4.18
39
 */
40
class Sitemap
41
{
42
    /**
43
     * @event IncludeSitemapEntryEvent The event that is triggered when an entry is
44
     * about to be included in a sitemap
45
     *
46
     * ---
47
     * ```php
48
     * use nystudio107\seomatic\events\IncludeSitemapEntryEvent;
49
     * use nystudio107\seomatic\helpers\Sitemap;
50
     * use yii\base\Event;
51
     * Event::on(Sitemap::class, Sitemap::EVENT_INCLUDE_SITEMAP_ENTRY, function(IncludeSitemapEntryEvent $e) {
52
     *     $e->include = false;
53
     * });
54
     * ```
55
     */
56
    public const EVENT_INCLUDE_SITEMAP_ENTRY = 'includeSitemapEntry';
57
58
    /**
59
     * Generate a sitemap with the passed in $params
60
     *
61
     * @param array $params
62
     * @return void
63
     * @throws SiteNotFoundException
64
     */
65
    public static function generateSitemap(array $params)
66
    {
67
        /** @var Queue $queue */
68
        $queue = $params['queue'] ?? null;
0 ignored issues
show
Unused Code introduced by
The assignment to $queue is dead and can be removed.
Loading history...
69
        /** @var GenerateSitemap|null $job */
70
        $job = $params['job'] ?? null;
71
72
        if (!$job) {
73
            $groupId = $params['groupId'];
74
            $siteId = $params['siteId'];
75
            $handle = $params['handle'];
76
            $type = $params['type'];
77
            $queueJobCacheKey = $params['queueJobCacheKey'];
78
        } else {
79
            $groupId = $job->groupId;
80
            $siteId = $job->siteId;
81
            $handle = $job->handle;
82
            $type = $job->type;
83
            $queueJobCacheKey = $job->queueJobCacheKey;
84
        }
85
86
        // Get an array of site ids for this site group
87
        $groupSiteIds = [];
88
89
        if (Seomatic::$settings->siteGroupsSeparate) {
90
            if (empty($groupId)) {
91
                try {
92
                    $thisSite = Craft::$app->getSites()->getSiteById($siteId);
93
                    if ($thisSite !== null) {
94
                        $group = $thisSite->getGroup();
95
                        $groupId = $group->id;
96
                    }
97
                } catch (Throwable $e) {
98
                    Craft::error($e->getMessage(), __METHOD__);
99
                }
100
            }
101
            $siteGroup = Craft::$app->getSites()->getGroupById($groupId);
102
            if ($siteGroup !== null) {
103
                $groupSiteIds = $siteGroup->getSiteIds();
104
            }
105
        }
106
107
        if (empty($groupSiteIds)) {
108
            $groupSiteIds = Craft::$app->getSites()->allSiteIds;
109
        }
110
111
        // Output some info if this is a console app
112
        if ($job && Craft::$app instanceof ConsoleApplication) {
113
            echo $job->description . PHP_EOL;
114
        }
115
116
        $lines = [];
117
        // Sitemap index XML header and opening tag
118
        $lines[] = '<?xml version="1.0" encoding="UTF-8"?>';
119
        $lines[] = '<?xml-stylesheet type="text/xsl" href="sitemap.xsl"?>';
120
        // One sitemap entry for each element
121
        $metaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceHandle(
122
            $type,
123
            $handle,
124
            $siteId
125
        );
126
        // If it doesn't exist, exit
127
        if ($metaBundle === null) {
128
            return;
129
        }
130
        $multiSite = count($metaBundle->sourceAltSiteSettings) > 1;
131
        $totalElements = null;
132
        $urlsetLine = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
133
        if ($metaBundle->metaSitemapVars->sitemapAssets) {
134
            $urlsetLine .= ' xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"';
135
            $urlsetLine .= ' xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"';
136
        }
137
        if ($multiSite) {
138
            $urlsetLine .= ' xmlns:xhtml="http://www.w3.org/1999/xhtml"';
139
        }
140
        if ((bool)$metaBundle->metaSitemapVars->newsSitemap) {
141
            $urlsetLine .= ' xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"';
142
        }
143
        $urlsetLine .= '>';
144
        $lines[] = $urlsetLine;
145
        // Get all of the elements for this meta bundle type
146
        $seoElement = Seomatic::$plugin->seoElements->getSeoElementByMetaBundleType($metaBundle->sourceBundleType);
147
        if ($seoElement !== null) {
148
            // Ensure `null` so that the resulting element query is correct
149
            if (empty($metaBundle->metaSitemapVars->sitemapLimit)) {
150
                $metaBundle->metaSitemapVars->sitemapLimit = null;
151
            }
152
            $totalElements = $seoElement::sitemapElementsQuery($metaBundle)->count();
153
            if ($metaBundle->metaSitemapVars->sitemapLimit && ($totalElements > $metaBundle->metaSitemapVars->sitemapLimit)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metaBundle->metaSitemapVars->sitemapLimit 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...
154
                $totalElements = $metaBundle->metaSitemapVars->sitemapLimit;
155
            }
156
        }
157
        // If no elements exist, just exit
158
        if (!$totalElements) {
159
            return;
160
        }
161
        // Stash the sitemap attributes so they can be modified on a per-entry basis
162
        $stashedSitemapAttrs = $metaBundle->metaSitemapVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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

162
        /** @scrutinizer ignore-call */ 
163
        $stashedSitemapAttrs = $metaBundle->metaSitemapVars->getAttributes();

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...
163
        $stashedGlobalVarsAttrs = $metaBundle->metaGlobalVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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

163
        /** @scrutinizer ignore-call */ 
164
        $stashedGlobalVarsAttrs = $metaBundle->metaGlobalVars->getAttributes();

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...
164
        // Use craft\db\Paginator to paginate the results so we don't exceed any memory limits
165
        // See batch() and each() discussion here: https://github.com/yiisoft/yii2/issues/8420
166
        // and here: https://github.com/craftcms/cms/issues/7338
167
        $paginator = new Paginator($seoElement::sitemapElementsQuery($metaBundle), [
168
            'pageSize' => GenerateSitemap::SITEMAP_QUERY_PAGE_SIZE,
169
        ]);
170
        $currentElement = 0;
171
        // Iterate through the paginated results
172
        while ($currentElement < $totalElements) {
173
            $elements = $paginator->getPageResults();
174
            if (Craft::$app instanceof ConsoleApplication) {
175
                echo 'Query ' . $paginator->getCurrentPage() . '/' . $paginator->getTotalPages()
176
                    . ' - elements: ' . $paginator->getTotalResults()
177
                    . PHP_EOL;
178
            }
179
            /** @var Element $element */
180
            foreach ($elements as $element) {
181
                if ($job) {
182
                    $job->updateProgress($currentElement++ / $totalElements);
183
                } else {
184
                    $currentElement++;
185
                }
186
                // Output some info if this is a console app
187
                if (Craft::$app instanceof ConsoleApplication) {
188
                    echo "Processing element {$currentElement}/{$totalElements} - {$element->title}" . PHP_EOL;
189
                }
190
191
                $metaBundle->metaSitemapVars->setAttributes($stashedSitemapAttrs, false);
192
                $metaBundle->metaGlobalVars->setAttributes($stashedGlobalVarsAttrs, false);
193
                // Combine in any per-entry type settings
194
                self::combineEntryTypeSettings($seoElement, $element, $metaBundle);
195
                // Make sure this entry isn't disabled
196
                self::combineFieldSettings($element, $metaBundle);
197
                // Special case for the __home__ URI
198
                $path = ($element->uri === '__home__') ? '' : $element->uri;
199
                // Check to see if robots is `none` or `no index`
200
                $robotsEnabled = true;
201
                if (!empty($metaBundle->metaGlobalVars->robots)) {
202
                    $robotsEnabled = $metaBundle->metaGlobalVars->robots !== 'none' &&
203
                        $metaBundle->metaGlobalVars->robots !== 'noindex';
204
                }
205
                $enabled = $element->getEnabledForSite($metaBundle->sourceSiteId);
206
                $enabled = $enabled && $path !== null && $metaBundle->metaSitemapVars->sitemapUrls && $robotsEnabled;
207
                $event = new IncludeSitemapEntryEvent([
208
                    'include' => $enabled,
209
                    'element' => $element,
210
                    'metaBundle' => $metaBundle,
211
                ]);
212
                Event::trigger(self::class, self::EVENT_INCLUDE_SITEMAP_ENTRY, $event);
213
                // Only add in a sitemap entry if it meets our criteria
214
                if ($event->include) {
215
                    // Get the url and canonicalUrl
216
                    try {
217
                        $url = UrlHelper::siteUrl($path, null, null, $metaBundle->sourceSiteId);
218
                    } catch (Exception $e) {
219
                        $url = '';
220
                    }
221
                    $url = UrlHelper::absoluteUrlWithProtocol($url);
222
                    if (Seomatic::$settings->excludeNonCanonicalUrls) {
223
                        Seomatic::$matchedElement = $element;
224
                        MetaValue::cache();
225
                        $path = $metaBundle->metaGlobalVars->parsedValue('canonicalUrl');
226
                        try {
227
                            $canonicalUrl = UrlHelper::siteUrl($path, null, null, $metaBundle->sourceSiteId);
228
                        } catch (Exception $e) {
229
                            $canonicalUrl = '';
230
                        }
231
                        $canonicalUrl = UrlHelper::absoluteUrlWithProtocol($canonicalUrl);
232
                        if ($url !== $canonicalUrl) {
233
                            Craft::info("Excluding URL: {$url} from the sitemap because it does not match the Canonical URL: {$canonicalUrl} - " . $metaBundle->metaGlobalVars->canonicalUrl . " - " . $element->uri);
0 ignored issues
show
Bug introduced by
Are you sure $metaBundle->metaGlobalVars->canonicalUrl of type object|string can be used in concatenation? ( Ignorable by Annotation )

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

233
                            Craft::info("Excluding URL: {$url} from the sitemap because it does not match the Canonical URL: {$canonicalUrl} - " . /** @scrutinizer ignore-type */ $metaBundle->metaGlobalVars->canonicalUrl . " - " . $element->uri);
Loading history...
234
                            continue;
235
                        }
236
                    }
237
                    $dateUpdated = $element->dateUpdated ?? $element->dateCreated ?? new DateTime();
238
                    $lines[] = '<url>';
239
                    // Standard sitemap key/values
240
                    $lines[] = '<loc>';
241
                    $lines[] = Html::encode($url);
242
                    $lines[] = '</loc>';
243
                    $lines[] = '<lastmod>';
244
                    $lines[] = $dateUpdated->format(DateTime::W3C);
245
                    $lines[] = '</lastmod>';
246
                    $lines[] = '<changefreq>';
247
                    $lines[] = $metaBundle->metaSitemapVars->sitemapChangeFreq;
248
                    $lines[] = '</changefreq>';
249
                    $lines[] = '<priority>';
250
                    $lines[] = $metaBundle->metaSitemapVars->sitemapPriority;
251
                    $lines[] = '</priority>';
252
                    // Handle alternate URLs if this is multi-site
253
                    if ($multiSite && $metaBundle->metaSitemapVars->sitemapAltLinks) {
254
                        $primarySiteId = Craft::$app->getSites()->getPrimarySite()->id;
255
                        foreach ($metaBundle->sourceAltSiteSettings as $altSiteSettings) {
256
                            if (in_array($altSiteSettings['siteId'], $groupSiteIds, false) && SiteHelper::siteEnabledWithUrls($altSiteSettings['siteId'])) {
257
                                $altElement = null;
258
                                if ($seoElement !== null) {
259
                                    /** @var Element $altElement */
260
                                    $altElement = $seoElement::sitemapAltElement(
261
                                        $metaBundle,
262
                                        $element->id,
263
                                        $altSiteSettings['siteId']
264
                                    );
265
                                }
266
                                // Make sure to only include the `hreflang` if the element exists,
267
                                // and sitemaps are on for it
268
                                if (Seomatic::$settings->addHrefLang && $altElement && $altElement->url) {
269
                                    list($altSourceId, $altSourceBundleType, $altSourceHandle, $altSourceSiteId, $altTypeId)
270
                                        = Seomatic::$plugin->metaBundles->getMetaSourceFromElement($altElement);
271
                                    $altMetaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceId(
272
                                        $altSourceBundleType,
273
                                        $altSourceId,
274
                                        $altSourceSiteId
275
                                    );
276
                                    if ($altMetaBundle) {
277
                                        $altEnabled = $altElement->getEnabledForSite($altMetaBundle->sourceSiteId);
278
                                        // Make sure this entry isn't disabled
279
                                        self::combineFieldSettings($altElement, $altMetaBundle);
280
                                        if ($altEnabled && $altMetaBundle->metaSitemapVars->sitemapUrls) {
281
                                            try {
282
                                                $altUrl = UrlHelper::siteUrl($altElement->url, null, null, $altSourceId);
283
                                            } catch (Exception $e) {
284
                                                $altUrl = $altElement->url;
285
                                            }
286
                                            $altUrl = UrlHelper::absoluteUrlWithProtocol($altUrl);
287
                                            // If this is the primary site, add it as x-default, too
288
                                            if ($primarySiteId === $altSourceSiteId && Seomatic::$settings->addXDefaultHrefLang) {
289
                                                $lines[] = '<xhtml:link rel="alternate"'
290
                                                    . ' hreflang="x-default"'
291
                                                    . ' href="' . Html::encode($altUrl) . '"'
292
                                                    . ' />';
293
                                            }
294
                                            $lines[] = '<xhtml:link rel="alternate"'
295
                                                . ' hreflang="' . $altSiteSettings['language'] . '"'
296
                                                . ' href="' . Html::encode($altUrl) . '"'
297
                                                . ' />';
298
                                        }
299
                                    }
300
                                }
301
                            }
302
                        }
303
                    }
304
                    // Handle news sitemaps https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap
305
                    if ((bool)$metaBundle->metaSitemapVars->newsSitemap) {
306
                        $now = new DateTime();
307
                        $interval = $now->diff($dateUpdated);
308
                        if ($interval->days <= 2) {
309
                            $language = strtolower($element->getLanguage());
310
                            if (!str_starts_with($language, 'zh')) {
311
                                $language = substr($language, 0, 2);
312
                            }
313
                            $lines[] = '<news:news>';
314
                            $lines[] = '<news:publication>';
315
                            $lines[] = '<news:name>' . $metaBundle->metaSitemapVars->newsPublicationName . '</news:name>';
316
                            $lines[] = '<news:language>' . $language . '</news:language>';
317
                            $lines[] = '</news:publication>';
318
                            $lines[] = '<news:publication_date>' . $dateUpdated->format(DateTime::W3C) . '</news:publication_date>';
319
                            $lines[] = '<news:title>' . $element->title . '</news:title>';
320
                            $lines[] = '</news:news>';
321
                        }
322
                    }
323
                    // Handle any Assets
324
                    if ($metaBundle->metaSitemapVars->sitemapAssets) {
325
                        // Regular Assets fields
326
                        $assetFields = FieldHelper::fieldsOfTypeFromElement(
327
                            $element,
328
                            FieldHelper::ASSET_FIELD_CLASS_KEY,
329
                            true
330
                        );
331
                        foreach ($assetFields as $assetField) {
332
                            $assets = $element[$assetField]->all();
333
                            /** @var Asset[] $assets */
334
                            foreach ($assets as $asset) {
335
                                self::assetSitemapItem($asset, $metaBundle, $lines);
336
                            }
337
                        }
338
                        // Assets embeded in Block fields
339
                        $blockFields = FieldHelper::fieldsOfTypeFromElement(
340
                            $element,
341
                            FieldHelper::BLOCK_FIELD_CLASS_KEY,
342
                            true
343
                        );
344
                        foreach ($blockFields as $blockField) {
345
                            $blocks = $element[$blockField]->all();
346
                            /** @var MatrixBlock[]|NeoBlock[]|SuperTableBlock[]|object[] $blocks */
347
                            foreach ($blocks as $block) {
348
                                $assetFields = [];
349
                                if ($block instanceof MatrixBlock) {
350
                                    $assetFields = FieldHelper::matrixFieldsOfType($block, AssetsField::class);
351
                                }
352
                                if ($block instanceof NeoBlock) {
353
                                    $assetFields = FieldHelper::neoFieldsOfType($block, AssetsField::class);
354
                                }
355
                                if ($block instanceof SuperTableBlock) {
356
                                    $assetFields = FieldHelper::superTableFieldsOfType($block, AssetsField::class);
357
                                }
358
                                foreach ($assetFields as $assetField) {
359
                                    foreach ($block[$assetField]->all() as $asset) {
360
                                        self::assetSitemapItem($asset, $metaBundle, $lines);
361
                                    }
362
                                }
363
                            }
364
                        }
365
                    }
366
                    $lines[] = '</url>';
367
                }
368
                // Include links to any known file types in the assets fields
369
                if ($metaBundle->metaSitemapVars->sitemapFiles) {
370
                    // Regular Assets fields
371
                    $assetFields = FieldHelper::fieldsOfTypeFromElement(
372
                        $element,
373
                        FieldHelper::ASSET_FIELD_CLASS_KEY,
374
                        true
375
                    );
376
                    foreach ($assetFields as $assetField) {
377
                        $assets = $element[$assetField]->all();
378
                        foreach ($assets as $asset) {
379
                            self::assetFilesSitemapLink($asset, $metaBundle, $lines);
380
                        }
381
                    }
382
                    // Assets embeded in Block fields
383
                    $blockFields = FieldHelper::fieldsOfTypeFromElement(
384
                        $element,
385
                        FieldHelper::BLOCK_FIELD_CLASS_KEY,
386
                        true
387
                    );
388
                    foreach ($blockFields as $blockField) {
389
                        $blocks = $element[$blockField]->all();
390
                        /** @var MatrixBlock[]|NeoBlock[]|SuperTableBlock[]|object[] $blocks */
391
                        foreach ($blocks as $block) {
392
                            $assetFields = [];
393
                            if ($block instanceof MatrixBlock) {
394
                                $assetFields = FieldHelper::matrixFieldsOfType($block, AssetsField::class);
395
                            }
396
                            if ($block instanceof SuperTableBlock) {
397
                                $assetFields = FieldHelper::superTableFieldsOfType($block, AssetsField::class);
398
                            }
399
                            if ($block instanceof NeoBlock) {
400
                                $assetFields = FieldHelper::neoFieldsOfType($block, AssetsField::class);
401
                            }
402
                            foreach ($assetFields as $assetField) {
403
                                foreach ($block[$assetField]->all() as $asset) {
404
                                    self::assetFilesSitemapLink($asset, $metaBundle, $lines);
405
                                }
406
                            }
407
                        }
408
                    }
409
                }
410
            }
411
            $paginator->currentPage++;
412
        }
413
        // Sitemap closing tag
414
        $lines[] = '</urlset>';
415
416
        $cache = Craft::$app->getCache();
417
        $cacheKey = SitemapTemplate::CACHE_KEY . $groupId . $type . $handle . $siteId;
418
        $dependency = new TagDependency([
419
            'tags' => [
420
                SitemapTemplate::GLOBAL_SITEMAP_CACHE_TAG,
421
                SitemapTemplate::SITEMAP_CACHE_TAG . $handle . $siteId,
422
            ],
423
        ]);
424
        $lines = implode('', $lines);
425
        // Cache sitemap cache; we use this instead of Seomatic::$cacheDuration because for
426
        // Control Panel requests, we set Seomatic::$cacheDuration = 1 so that they are never
427
        // cached
428
        $cacheDuration = Seomatic::$devMode
429
            ? Seomatic::DEVMODE_CACHE_DURATION
430
            : null;
431
        $result = $cache->set($cacheKey, $lines, $cacheDuration, $dependency);
432
        // Remove the queue job id from the cache too
433
        $cache->delete($queueJobCacheKey);
434
        Craft::debug('Sitemap cache result: ' . print_r($result, true) . ' for cache key: ' . $cacheKey, __METHOD__);
0 ignored issues
show
Bug introduced by
Are you sure print_r($result, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

434
        Craft::debug('Sitemap cache result: ' . /** @scrutinizer ignore-type */ print_r($result, true) . ' for cache key: ' . $cacheKey, __METHOD__);
Loading history...
435
        // Output some info if this is a console app
436
        if (Craft::$app instanceof ConsoleApplication) {
437
            echo 'Sitemap cache result: ' . print_r($result, true) . ' for cache key: ' . $cacheKey . PHP_EOL;
438
        }
439
        // If the FastCGI Cache Bust plugin is installed, clear its caches too
440
        /** @var ?FastcgiCacheBust $plugin */
441
        $plugin = Craft::$app->getPlugins()->getPlugin('fastcgi-cache-bust');
442
        if ($plugin !== null) {
443
            // FastcgiCacheBust has an error in its PHPdoc
444
            /** @phpstan-ignore-next-line */
445
            $plugin->cache->clearAll();
446
        }
447
    }
448
449
450
    /**
451
     * Combine any per-entry type field settings from $element with the passed in
452
     * $metaBundle
453
     *
454
     * @param SeoElementInterface|string $seoElement
455
     * @param Element $element
456
     * @param MetaBundle $metaBundle
457
     */
458
    protected static function combineEntryTypeSettings($seoElement, Element $element, MetaBundle $metaBundle)
459
    {
460
        if (!empty($seoElement::typeMenuFromHandle($metaBundle->sourceHandle))) {
461
            list($sourceId, $sourceBundleType, $sourceHandle, $sourceSiteId, $typeId)
462
                = Seomatic::$plugin->metaBundles->getMetaSourceFromElement($element);
463
            $entryTypeBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceId(
464
                $sourceBundleType,
465
                $sourceId,
466
                $sourceSiteId,
467
                $typeId
468
            );
469
            // Combine in any settings for this entry type
470
            if ($entryTypeBundle) {
471
                // Combine the meta sitemap vars
472
                $attributes = $entryTypeBundle->metaSitemapVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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

472
                /** @scrutinizer ignore-call */ 
473
                $attributes = $entryTypeBundle->metaSitemapVars->getAttributes();

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...
473
                $attributes = array_filter(
474
                    $attributes,
475
                    [ArrayHelper::class, 'preserveBools']
476
                );
477
                $metaBundle->metaSitemapVars->setAttributes($attributes, false);
478
479
                // Combine the meta global vars
480
                $attributes = $entryTypeBundle->metaGlobalVars->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() 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

480
                /** @scrutinizer ignore-call */ 
481
                $attributes = $entryTypeBundle->metaGlobalVars->getAttributes();

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...
481
                $attributes = array_filter(
482
                    $attributes,
483
                    [ArrayHelper::class, 'preserveBools']
484
                );
485
                $metaBundle->metaGlobalVars->setAttributes($attributes, false);
486
            }
487
        }
488
    }
489
490
    /**
491
     * Combine any SEO Settings field settings from $element with the passed in
492
     * $metaBundle
493
     *
494
     * @param Element $element
495
     * @param MetaBundle $metaBundle
496
     */
497
    protected static function combineFieldSettings(Element $element, MetaBundle $metaBundle)
498
    {
499
        $fieldHandles = FieldHelper::fieldsOfTypeFromElement(
500
            $element,
501
            FieldHelper::SEO_SETTINGS_CLASS_KEY,
502
            true
503
        );
504
        foreach ($fieldHandles as $fieldHandle) {
505
            if (!empty($element->$fieldHandle)) {
506
                /** @var SeoSettings $seoSettingsField */
507
                $seoSettingsField = Craft::$app->getFields()->getFieldByHandle($fieldHandle);
508
                /** @var MetaBundle $metaBundle */
509
                $fieldMetaBundle = $element->$fieldHandle;
510
                if ($seoSettingsField !== null) {
511
                    if ($seoSettingsField->sitemapTabEnabled) {
512
                        Seomatic::$plugin->metaBundles->pruneFieldMetaBundleSettings($fieldMetaBundle, $fieldHandle);
513
                        // Combine the meta sitemap vars
514
                        $attributes = $fieldMetaBundle->metaSitemapVars->getAttributes();
515
516
                        // Get the explicitly inherited attributes
517
                        $inherited = array_keys(ArrayHelper::remove($attributes, 'inherited', []));
0 ignored issues
show
Bug introduced by
It seems like nystudio107\seomatic\hel..., 'inherited', array()) can also be of type null; however, parameter $array of array_keys() does only seem to accept array, 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

517
                        $inherited = array_keys(/** @scrutinizer ignore-type */ ArrayHelper::remove($attributes, 'inherited', []));
Loading history...
518
519
                        $attributes = array_intersect_key(
520
                            $attributes,
521
                            array_flip((array)$seoSettingsField->sitemapEnabledFields)
522
                        );
523
                        $attributes = array_filter(
524
                            $attributes,
525
                            [ArrayHelper::class, 'preserveBools']
526
                        );
527
528
                        foreach ($inherited as $inheritedAttribute) {
529
                            unset($attributes[$inheritedAttribute]);
530
                        }
531
532
                        $metaBundle->metaSitemapVars->setAttributes($attributes, false);
533
                    }
534
                    // Combine the meta global vars
535
                    $attributes = $fieldMetaBundle->metaGlobalVars->getAttributes();
536
                    $attributes = array_filter(
537
                        $attributes,
538
                        [ArrayHelper::class, 'preserveBools']
539
                    );
540
                    $metaBundle->metaGlobalVars->setAttributes($attributes, false);
541
                }
542
            }
543
        }
544
    }
545
546
    /**
547
     * @param Asset $asset
548
     * @param MetaBundle $metaBundle
549
     * @param array $lines
550
     */
551
    protected static function assetSitemapItem(Asset $asset, MetaBundle $metaBundle, array &$lines)
552
    {
553
        if ((bool)$asset->enabledForSite && $asset->getUrl() !== null) {
554
            switch ($asset->kind) {
555
                case 'image':
556
                    $lines[] = '<image:image>';
557
                    $lines[] = '<image:loc>';
558
                    $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
559
                    $lines[] = '</image:loc>';
560
                    // Handle the dynamic field => property mappings
561
                    foreach ($metaBundle->metaSitemapVars->sitemapImageFieldMap as $row) {
562
                        $fieldName = $row['field'] ?? '';
563
                        $propName = $row['property'] ?? '';
564
                        if (!empty($fieldName) && !empty($asset[$fieldName]) && !empty($propName)) {
565
                            $lines[] = '<image:' . $propName . '>';
566
                            $lines[] = Html::encode($asset[$fieldName]);
567
                            $lines[] = '</image:' . $propName . '>';
568
                        }
569
                    }
570
                    $lines[] = '</image:image>';
571
                    break;
572
573
                case 'video':
574
                    $lines[] = '<video:video>';
575
                    $lines[] = '<video:content_loc>';
576
                    $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
577
                    $lines[] = '</video:content_loc>';
578
                    // Handle the dynamic field => property mappings
579
                    foreach ($metaBundle->metaSitemapVars->sitemapVideoFieldMap as $row) {
580
                        $fieldName = $row['field'] ?? '';
581
                        $propName = $row['property'] ?? '';
582
                        if (!empty($fieldName) && !empty($asset[$fieldName]) && !empty($propName)) {
583
                            $lines[] = '<video:' . $propName . '>';
584
                            $lines[] = Html::encode($asset[$fieldName]);
585
                            $lines[] = '</video:' . $propName . '>';
586
                        }
587
                    }
588
                    $lines[] = '</video:video>';
589
                    break;
590
            }
591
        }
592
    }
593
594
    /**
595
     * @param Asset $asset
596
     * @param MetaBundle $metaBundle
597
     * @param array $lines
598
     */
599
    protected static function assetFilesSitemapLink(Asset $asset, MetaBundle $metaBundle, array &$lines)
600
    {
601
        if ((bool)$asset->enabledForSite && $asset->getUrl() !== null) {
602
            if (in_array($asset->kind, SitemapTemplate::FILE_TYPES, false)) {
603
                $dateUpdated = $asset->dateUpdated ?? $asset->dateCreated ?? new DateTime();
604
                $lines[] = '<url>';
605
                $lines[] = '<loc>';
606
                $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
607
                $lines[] = '</loc>';
608
                $lines[] = '<lastmod>';
609
                $lines[] = $dateUpdated->format(DateTime::W3C);
610
                $lines[] = '</lastmod>';
611
                $lines[] = '<changefreq>';
612
                $lines[] = $metaBundle->metaSitemapVars->sitemapChangeFreq;
613
                $lines[] = '</changefreq>';
614
                $lines[] = '<priority>';
615
                $lines[] = $metaBundle->metaSitemapVars->sitemapPriority;
616
                $lines[] = '</priority>';
617
                $lines[] = '</url>';
618
            }
619
        }
620
    }
621
}
622