Passed
Push — v3 ( 9af8ac...482808 )
by Andrew
20:05 queued 13s
created

Sitemap::assetSitemapItem()   C

Complexity

Conditions 13

Size

Total Lines 39
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 2
Bugs 2 Features 0
Metric Value
eloc 30
c 2
b 2
f 0
dl 0
loc 39
ccs 0
cts 31
cp 0
rs 6.6166
cc 13
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\console\Application as ConsoleApplication;
9
use craft\db\Paginator;
10
use craft\elements\Asset;
11
use craft\elements\MatrixBlock;
12
use craft\errors\SiteNotFoundException;
13
use craft\fields\Assets as AssetsField;
14
use craft\queue\Queue;
15
use DateTime;
16
use nystudio107\fastcgicachebust\FastcgiCacheBust;
17
use nystudio107\seomatic\base\SeoElementInterface;
18
use nystudio107\seomatic\events\IncludeSitemapEntryEvent;
19
use nystudio107\seomatic\fields\SeoSettings;
20
use nystudio107\seomatic\helpers\Field as FieldHelper;
21
use nystudio107\seomatic\jobs\GenerateSitemap;
22
use nystudio107\seomatic\models\MetaBundle;
23
use nystudio107\seomatic\models\SitemapTemplate;
24
use nystudio107\seomatic\Seomatic;
25
use Throwable;
26
use verbb\supertable\elements\SuperTableBlockElement as SuperTableBlock;
27
use yii\base\Event;
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
    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 = true;
206
                if (Seomatic::$craft34) {
207
                    $enabled = $element->getEnabledForSite($metaBundle->sourceSiteId);
208
                }
209
                $enabled = $enabled && $path !== null && $metaBundle->metaSitemapVars->sitemapUrls && $robotsEnabled;
210
                $event = new IncludeSitemapEntryEvent([
211
                    'include' => $enabled,
212
                    'element' => $element,
213
                    'metaBundle' => $metaBundle,
214
                ]);
215
                Event::trigger(self::class, self::EVENT_INCLUDE_SITEMAP_ENTRY, $event);
216
                // Only add in a sitemap entry if it meets our criteria
217
                if ($event->include) {
218
                    // Get the url and canonicalUrl
219
                    try {
220
                        $url = UrlHelper::siteUrl($path, null, null, $metaBundle->sourceSiteId);
221
                    } catch (Exception $e) {
222
                        $url = '';
223
                    }
224
                    $url = UrlHelper::absoluteUrlWithProtocol($url);
225
                    if (Seomatic::$settings->excludeNonCanonicalUrls) {
226
                        Seomatic::$matchedElement = $element;
227
                        MetaValue::cache();
228
                        $path = $metaBundle->metaGlobalVars->parsedValue('canonicalUrl');
229
                        try {
230
                            $canonicalUrl = UrlHelper::siteUrl($path, null, null, $metaBundle->sourceSiteId);
231
                        } catch (Exception $e) {
232
                            $canonicalUrl = '';
233
                        }
234
                        $canonicalUrl = UrlHelper::absoluteUrlWithProtocol($canonicalUrl);
235
                        if ($url !== $canonicalUrl) {
236
                            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

236
                            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...
237
                            continue;
238
                        }
239
                    }
240
                    $dateUpdated = $element->dateUpdated ?? $element->dateCreated ?? new DateTime();
241
                    $lines[] = '<url>';
242
                    // Standard sitemap key/values
243
                    $lines[] = '<loc>';
244
                    $lines[] = Html::encode($url);
245
                    $lines[] = '</loc>';
246
                    $lines[] = '<lastmod>';
247
                    $lines[] = $dateUpdated->format(DateTime::W3C);
248
                    $lines[] = '</lastmod>';
249
                    $lines[] = '<changefreq>';
250
                    $lines[] = $metaBundle->metaSitemapVars->sitemapChangeFreq;
251
                    $lines[] = '</changefreq>';
252
                    $lines[] = '<priority>';
253
                    $lines[] = $metaBundle->metaSitemapVars->sitemapPriority;
254
                    $lines[] = '</priority>';
255
                    // Handle alternate URLs if this is multi-site
256
                    if ($multiSite && $metaBundle->metaSitemapVars->sitemapAltLinks) {
257
                        $primarySiteId = Craft::$app->getSites()->getPrimarySite()->id;
258
                        foreach ($metaBundle->sourceAltSiteSettings as $altSiteSettings) {
259
                            if (in_array($altSiteSettings['siteId'], $groupSiteIds, false) && SiteHelper::siteEnabledWithUrls($altSiteSettings['siteId'])) {
260
                                $altElement = null;
261
                                if ($seoElement !== null) {
262
                                    /** @var Element $altElement */
263
                                    $altElement = $seoElement::sitemapAltElement(
264
                                        $metaBundle,
265
                                        $element->id,
266
                                        $altSiteSettings['siteId']
267
                                    );
268
                                }
269
                                // Make sure to only include the `hreflang` if the element exists,
270
                                // and sitemaps are on for it
271
                                if (Seomatic::$settings->addHrefLang && $altElement && $altElement->url) {
272
                                    list($altSourceId, $altSourceBundleType, $altSourceHandle, $altSourceSiteId, $altTypeId)
273
                                        = Seomatic::$plugin->metaBundles->getMetaSourceFromElement($altElement);
274
                                    $altMetaBundle = Seomatic::$plugin->metaBundles->getMetaBundleBySourceId(
275
                                        $altSourceBundleType,
276
                                        $altSourceId,
277
                                        $altSourceSiteId
278
                                    );
279
                                    if ($altMetaBundle) {
280
                                        $altEnabled = true;
281
                                        if (Seomatic::$craft34) {
282
                                            $altEnabled = $altElement->getEnabledForSite($altMetaBundle->sourceSiteId);
283
                                        }
284
                                        // Make sure this entry isn't disabled
285
                                        self::combineFieldSettings($altElement, $altMetaBundle);
286
                                        if ($altEnabled && $altMetaBundle->metaSitemapVars->sitemapUrls) {
287
                                            try {
288
                                                $altUrl = UrlHelper::siteUrl($altElement->url, null, null, $altSourceId);
0 ignored issues
show
Bug introduced by
It seems like $altElement->url can also be of type craft\base\ElementInterface[]; however, parameter $path of nystudio107\seomatic\helpers\UrlHelper::siteUrl() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

288
                                                $altUrl = UrlHelper::siteUrl(/** @scrutinizer ignore-type */ $altElement->url, null, null, $altSourceId);
Loading history...
289
                                            } catch (Exception $e) {
290
                                                $altUrl = $altElement->url;
291
                                            }
292
                                            $altUrl = UrlHelper::absoluteUrlWithProtocol($altUrl);
0 ignored issues
show
Bug introduced by
It seems like $altUrl can also be of type craft\base\ElementInterface[]; however, parameter $url of nystudio107\seomatic\hel...soluteUrlWithProtocol() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

292
                                            $altUrl = UrlHelper::absoluteUrlWithProtocol(/** @scrutinizer ignore-type */ $altUrl);
Loading history...
293
                                            // If this is the primary site, add it as x-default, too
294
                                            if ($primarySiteId === $altSourceSiteId && Seomatic::$settings->addXDefaultHrefLang) {
295
                                                $lines[] = '<xhtml:link rel="alternate"'
296
                                                    . ' hreflang="x-default"'
297
                                                    . ' href="' . Html::encode($altUrl) . '"'
298
                                                    . ' />';
299
                                            }
300
                                            $lines[] = '<xhtml:link rel="alternate"'
301
                                                . ' hreflang="' . $altSiteSettings['language'] . '"'
302
                                                . ' href="' . Html::encode($altUrl) . '"'
303
                                                . ' />';
304
                                        }
305
                                    }
306
                                }
307
                            }
308
                        }
309
                    }
310
                    // Handle news sitemaps https://developers.google.com/search/docs/crawling-indexing/sitemaps/news-sitemap
311
                    if ((bool)$metaBundle->metaSitemapVars->newsSitemap) {
312
                        $now = new DateTime();
313
                        $interval = $now->diff($dateUpdated);
314
                        if ($interval->days <= 2) {
315
                            $language = strtolower($element->getLanguage());
316
                            if (!str_starts_with($language, 'zh')) {
317
                                $language = substr($language, 0, 2);
318
                            }
319
                            $lines[] = '<news:news>';
320
                            $lines[] = '<news:publication>';
321
                            $lines[] = '<news:name>' . $metaBundle->metaSitemapVars->newsPublicationName . '</news:name>';
322
                            $lines[] = '<news:language>' . $language . '</news:language>';
323
                            $lines[] = '</news:publication>';
324
                            $lines[] = '<news:publication_date>' . $dateUpdated->format(DateTime::W3C) . '</news:publication_date>';
325
                            $lines[] = '<news:title>' . $element->title . '</news:title>';
326
                            $lines[] = '</news:news>';
327
                        }
328
                    }
329
                    // Handle any Assets
330
                    if ($metaBundle->metaSitemapVars->sitemapAssets) {
331
                        // Regular Assets fields
332
                        $assetFields = FieldHelper::fieldsOfTypeFromElement(
333
                            $element,
334
                            FieldHelper::ASSET_FIELD_CLASS_KEY,
335
                            true
336
                        );
337
                        foreach ($assetFields as $assetField) {
338
                            $assets = $element[$assetField]->all();
339
                            /** @var Asset[] $assets */
340
                            foreach ($assets as $asset) {
341
                                self::assetSitemapItem($asset, $metaBundle, $lines);
342
                            }
343
                        }
344
                        // Assets embeded in Block fields
345
                        $blockFields = FieldHelper::fieldsOfTypeFromElement(
346
                            $element,
347
                            FieldHelper::BLOCK_FIELD_CLASS_KEY,
348
                            true
349
                        );
350
                        foreach ($blockFields as $blockField) {
351
                            $blocks = $element[$blockField]->all();
352
                            /** @var MatrixBlock[]|NeoBlock[]|SuperTableBlock[]|object[] $blocks */
353
                            foreach ($blocks as $block) {
354
                                $assetFields = [];
355
                                if ($block instanceof MatrixBlock) {
356
                                    $assetFields = FieldHelper::matrixFieldsOfType($block, AssetsField::class);
357
                                }
358
                                if ($block instanceof NeoBlock) {
359
                                    $assetFields = FieldHelper::neoFieldsOfType($block, AssetsField::class);
360
                                }
361
                                if ($block instanceof SuperTableBlock) {
362
                                    $assetFields = FieldHelper::superTableFieldsOfType($block, AssetsField::class);
363
                                }
364
                                foreach ($assetFields as $assetField) {
365
                                    foreach ($block[$assetField]->all() as $asset) {
366
                                        self::assetSitemapItem($asset, $metaBundle, $lines);
367
                                    }
368
                                }
369
                            }
370
                        }
371
                    }
372
                    $lines[] = '</url>';
373
                }
374
                // Include links to any known file types in the assets fields
375
                if ($metaBundle->metaSitemapVars->sitemapFiles) {
376
                    // Regular Assets fields
377
                    $assetFields = FieldHelper::fieldsOfTypeFromElement(
378
                        $element,
379
                        FieldHelper::ASSET_FIELD_CLASS_KEY,
380
                        true
381
                    );
382
                    foreach ($assetFields as $assetField) {
383
                        $assets = $element[$assetField]->all();
384
                        foreach ($assets as $asset) {
385
                            self::assetFilesSitemapLink($asset, $metaBundle, $lines);
386
                        }
387
                    }
388
                    // Assets embeded in Block fields
389
                    $blockFields = FieldHelper::fieldsOfTypeFromElement(
390
                        $element,
391
                        FieldHelper::BLOCK_FIELD_CLASS_KEY,
392
                        true
393
                    );
394
                    foreach ($blockFields as $blockField) {
395
                        $blocks = $element[$blockField]->all();
396
                        /** @var MatrixBlock[]|NeoBlock[]|SuperTableBlock[]|object[] $blocks */
397
                        foreach ($blocks as $block) {
398
                            $assetFields = [];
399
                            if ($block instanceof MatrixBlock) {
400
                                $assetFields = FieldHelper::matrixFieldsOfType($block, AssetsField::class);
401
                            }
402
                            if ($block instanceof SuperTableBlock) {
403
                                $assetFields = FieldHelper::superTableFieldsOfType($block, AssetsField::class);
404
                            }
405
                            if ($block instanceof NeoBlock) {
406
                                $assetFields = FieldHelper::neoFieldsOfType($block, AssetsField::class);
407
                            }
408
                            foreach ($assetFields as $assetField) {
409
                                foreach ($block[$assetField]->all() as $asset) {
410
                                    self::assetFilesSitemapLink($asset, $metaBundle, $lines);
411
                                }
412
                            }
413
                        }
414
                    }
415
                }
416
            }
417
            $paginator->currentPage++;
418
        }
419
        // Sitemap closing tag
420
        $lines[] = '</urlset>';
421
422
        $cache = Craft::$app->getCache();
423
        $cacheKey = SitemapTemplate::CACHE_KEY . $groupId . $type . $handle . $siteId;
424
        $dependency = new TagDependency([
425
            'tags' => [
426
                SitemapTemplate::GLOBAL_SITEMAP_CACHE_TAG,
427
                SitemapTemplate::SITEMAP_CACHE_TAG . $handle . $siteId,
428
            ],
429
        ]);
430
        $lines = implode('', $lines);
431
        // Cache sitemap cache; we use this instead of Seomatic::$cacheDuration because for
432
        // Control Panel requests, we set Seomatic::$cacheDuration = 1 so that they are never
433
        // cached
434
        $cacheDuration = Seomatic::$devMode
435
            ? Seomatic::DEVMODE_CACHE_DURATION
436
            : null;
437
        $result = $cache->set($cacheKey, $lines, $cacheDuration, $dependency);
438
        // Remove the queue job id from the cache too
439
        $cache->delete($queueJobCacheKey);
440
        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

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

478
                /** @scrutinizer ignore-call */ 
479
                $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...
479
                $attributes = array_filter(
480
                    $attributes,
481
                    [ArrayHelper::class, 'preserveBools']
482
                );
483
                $metaBundle->metaSitemapVars->setAttributes($attributes, false);
484
485
                // Combine the meta global vars
486
                $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

486
                /** @scrutinizer ignore-call */ 
487
                $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...
487
                $attributes = array_filter(
488
                    $attributes,
489
                    [ArrayHelper::class, 'preserveBools']
490
                );
491
                $metaBundle->metaGlobalVars->setAttributes($attributes, false);
492
            }
493
        }
494
    }
495
496
    /**
497
     * Combine any SEO Settings field settings from $element with the passed in
498
     * $metaBundle
499
     *
500
     * @param Element $element
501
     * @param MetaBundle $metaBundle
502
     */
503
    protected static function combineFieldSettings(Element $element, MetaBundle $metaBundle)
504
    {
505
        $fieldHandles = FieldHelper::fieldsOfTypeFromElement(
506
            $element,
507
            FieldHelper::SEO_SETTINGS_CLASS_KEY,
508
            true
509
        );
510
        foreach ($fieldHandles as $fieldHandle) {
511
            if (!empty($element->$fieldHandle)) {
512
                /** @var SeoSettings $seoSettingsField */
513
                $seoSettingsField = Craft::$app->getFields()->getFieldByHandle($fieldHandle);
514
                /** @var MetaBundle $fieldMetaBundle */
515
                $fieldMetaBundle = $element->$fieldHandle;
516
                if ($seoSettingsField !== null) {
517
                    if ($seoSettingsField->sitemapTabEnabled) {
518
                        Seomatic::$plugin->metaBundles->pruneFieldMetaBundleSettings($fieldMetaBundle, $fieldHandle);
519
                        // Combine the meta sitemap vars
520
                        $attributes = $fieldMetaBundle->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

520
                        /** @scrutinizer ignore-call */ 
521
                        $attributes = $fieldMetaBundle->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...
521
522
                        // Get the explicitly inherited attributes
523
                        $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

523
                        $inherited = array_keys(/** @scrutinizer ignore-type */ ArrayHelper::remove($attributes, 'inherited', []));
Loading history...
524
525
                        $attributes = array_intersect_key(
526
                            $attributes,
527
                            array_flip($seoSettingsField->sitemapEnabledFields)
528
                        );
529
                        $attributes = array_filter(
530
                            $attributes,
531
                            [ArrayHelper::class, 'preserveBools']
532
                        );
533
534
                        foreach ($inherited as $inheritedAttribute) {
535
                            unset($attributes[$inheritedAttribute]);
536
                        }
537
538
                        $metaBundle->metaSitemapVars->setAttributes($attributes, false);
539
                    }
540
                    // Combine the meta global vars
541
                    $attributes = $fieldMetaBundle->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

541
                    /** @scrutinizer ignore-call */ 
542
                    $attributes = $fieldMetaBundle->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...
542
                    $attributes = array_filter(
543
                        $attributes,
544
                        [ArrayHelper::class, 'preserveBools']
545
                    );
546
                    $metaBundle->metaGlobalVars->setAttributes($attributes, false);
547
                }
548
            }
549
        }
550
    }
551
552
    /**
553
     * @param Asset $asset
554
     * @param MetaBundle $metaBundle
555
     * @param array $lines
556
     */
557
    protected static function assetSitemapItem(Asset $asset, MetaBundle $metaBundle, array &$lines)
558
    {
559
        if ((bool)$asset->enabledForSite && $asset->getUrl() !== null) {
560
            switch ($asset->kind) {
561
                case 'image':
562
                    $lines[] = '<image:image>';
563
                    $lines[] = '<image:loc>';
564
                    $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
565
                    $lines[] = '</image:loc>';
566
                    // Handle the dynamic field => property mappings
567
                    foreach ($metaBundle->metaSitemapVars->sitemapImageFieldMap as $row) {
568
                        $fieldName = $row['field'] ?? '';
569
                        $propName = $row['property'] ?? '';
570
                        if (!empty($fieldName) && !empty($asset[$fieldName]) && !empty($propName)) {
571
                            $lines[] = '<image:' . $propName . '>';
572
                            $lines[] = Html::encode($asset[$fieldName]);
573
                            $lines[] = '</image:' . $propName . '>';
574
                        }
575
                    }
576
                    $lines[] = '</image:image>';
577
                    break;
578
579
                case 'video':
580
                    $lines[] = '<video:video>';
581
                    $lines[] = '<video:content_loc>';
582
                    $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
583
                    $lines[] = '</video:content_loc>';
584
                    // Handle the dynamic field => property mappings
585
                    foreach ($metaBundle->metaSitemapVars->sitemapVideoFieldMap as $row) {
586
                        $fieldName = $row['field'] ?? '';
587
                        $propName = $row['property'] ?? '';
588
                        if (!empty($fieldName) && !empty($asset[$fieldName]) && !empty($propName)) {
589
                            $lines[] = '<video:' . $propName . '>';
590
                            $lines[] = Html::encode($asset[$fieldName]);
591
                            $lines[] = '</video:' . $propName . '>';
592
                        }
593
                    }
594
                    $lines[] = '</video:video>';
595
                    break;
596
            }
597
        }
598
    }
599
600
    /**
601
     * @param Asset $asset
602
     * @param MetaBundle $metaBundle
603
     * @param array $lines
604
     */
605
    protected static function assetFilesSitemapLink(Asset $asset, MetaBundle $metaBundle, array &$lines)
606
    {
607
        if ((bool)$asset->enabledForSite && $asset->getUrl() !== null) {
608
            if (in_array($asset->kind, SitemapTemplate::FILE_TYPES, false)) {
609
                $dateUpdated = $asset->dateUpdated ?? $asset->dateCreated ?? new DateTime();
610
                $lines[] = '<url>';
611
                $lines[] = '<loc>';
612
                $lines[] = Html::encode(UrlHelper::absoluteUrlWithProtocol($asset->getUrl()));
613
                $lines[] = '</loc>';
614
                $lines[] = '<lastmod>';
615
                $lines[] = $dateUpdated->format(DateTime::W3C);
616
                $lines[] = '</lastmod>';
617
                $lines[] = '<changefreq>';
618
                $lines[] = $metaBundle->metaSitemapVars->sitemapChangeFreq;
619
                $lines[] = '</changefreq>';
620
                $lines[] = '<priority>';
621
                $lines[] = $metaBundle->metaSitemapVars->sitemapPriority;
622
                $lines[] = '</priority>';
623
                $lines[] = '</url>';
624
            }
625
        }
626
    }
627
}
628