• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / Yoast-SEO-for-TYPO3 / 41799098350

04 Nov 2026 08:23AM UTC coverage: 21.191% (+0.3%) from 20.893%
41799098350

push

github

web-flow
Merge pull request #690 from Yoast/bugfix/681-frontend-cache

[BUGFIX] Prevent Yoast frontend requests to end up in cache,

6 of 16 new or added lines in 4 files covered. (37.5%)

580 of 2737 relevant lines covered (21.19%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/StructuredData/StructuredDataProviderManager.php
1
<?php
2

3
/**
4
 * This file is part of the "yoast_seo" extension for TYPO3 CMS.
5
 *
6
 * For the full copyright and license information, please read the
7
 * LICENSE.txt file that was distributed with this source code.
8
 */
9

10
declare(strict_types=1);
11

12
namespace YoastSeoForTypo3\YoastSeo\StructuredData;
13

14
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
15
use TYPO3\CMS\Core\Service\DependencyOrderingService;
16
use TYPO3\CMS\Core\SingletonInterface;
17
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use YoastSeoForTypo3\YoastSeo\Service\Frontend\FrontendServiceInterface;
20

21
class StructuredDataProviderManager implements SingletonInterface
22
{
23
    public function __construct(
24
        protected FrontendInterface $pageCache,
25
        protected FrontendServiceInterface $frontendService,
26
        protected DependencyOrderingService $dependencyOrderingService,
27
        protected TypoScriptService $typoScriptService,
28
    ) {}
×
29

30
    /**
31
     * @param array<string, mixed> $params
32
     */
33
    public function render(array &$params, object $pObj): void
34
    {
35
        if (!$this->frontendService->isFrontendRequest()) {
×
36
            return;
×
37
        }
38

39
        $data = $this->getStructuredData();
×
40

41
        $params['headerData']['StructuredDataManager']
×
42
            = $this->getSourceComment() . PHP_EOL . $this->buildJsonLdBlob($data);
×
43
    }
44

45
    /**
46
     * @param array<string, array<string, mixed>> $src
47
     */
48
    protected function buildJsonLdBlob(array $src): string
49
    {
50
        $data = $src ? array_merge(...array_values($src)) : [];
×
51

52
        return $data
×
53
            ? '<script type="application/ld+json">' . json_encode($data) . '</script>'
×
54
            : '';
×
55
    }
56

57
    /**
58
     * @return array<string, array<string, mixed>>
59
     */
60
    public function getStructuredData(): array
61
    {
62
        $structuredData = [];
×
63
        // TODO: move isCachingAllowed() into FrontendServiceInterface in the next major
NEW
64
        $useCache = !method_exists($this->frontendService, 'isCachingAllowed') || $this->frontendService->isCachingAllowed();
×
65
        foreach ($this->getOrderedStructuredDataProviders() as $provider => $configuration) {
×
66
            $cacheIdentifier = $this->frontendService->getCacheIdentifier('-structured-data-' . $provider);
×
NEW
67
            $data = $useCache ? $this->pageCache->get($cacheIdentifier) : false;
×
68
            if ($data !== false) {
×
69
                $structuredData[$provider] = $data;
×
70
                continue;
×
71
            }
72
            $structuredDataProviderObject = $this->getStructuredDataProviderObject($configuration);
×
73
            if ($structuredDataProviderObject === null) {
×
74
                continue;
×
75
            }
76

NEW
77
            $data = $structuredDataProviderObject->getData();
×
NEW
78
            if (empty($data)) {
×
NEW
79
                continue;
×
80
            }
NEW
81
            if ($useCache) {
×
82
                $this->pageCache->set(
×
83
                    $cacheIdentifier,
×
84
                    $data,
×
85
                    ['pageId_' . $this->frontendService->getPageUid()],
×
86
                    $this->frontendService->getCacheTimeout(),
×
87
                );
×
88
            }
NEW
89
            $structuredData[$provider] = $data;
×
90
        }
91

92
        return $structuredData;
×
93
    }
94

95
    /**
96
     * @param array<string, mixed> $configuration
97
     */
98
    protected function getStructuredDataProviderObject(array $configuration): StructuredDataProviderInterface|null
99
    {
100
        if (!class_exists($configuration['provider']) || !is_subclass_of($configuration['provider'], StructuredDataProviderInterface::class)) {
×
101
            return null;
×
102
        }
103

104
        $providerObject = GeneralUtility::makeInstance($configuration['provider']);
×
105
        if (method_exists($providerObject, 'setConfiguration')) {
×
106
            $providerObject->setConfiguration($configuration);
×
107
        }
108
        return $providerObject;
×
109
    }
110

111
    /**
112
     * @return array<string, array<string, mixed>>
113
     */
114
    private function getOrderedStructuredDataProviders(): array
115
    {
116
        $structuredDataProviders = $this->getStructuredDataProviderConfiguration();
×
117
        $structuredDataProviders = $this->setProviderOrder($structuredDataProviders);
×
118

119
        return $this->dependencyOrderingService->orderByDependencies($structuredDataProviders);
×
120
    }
121

122
    /**
123
     * @return array<string, array<string, mixed>>
124
     */
125
    private function getStructuredDataProviderConfiguration(): array
126
    {
127
        $config = $this->typoScriptService->convertTypoScriptArrayToPlainArray(
×
128
            $this->frontendService->getTyposcriptConfiguration()
×
129
        );
×
130

131
        return $config['structuredData']['providers'] ?? [];
×
132
    }
133

134
    /**
135
     * @param array<string, array<string, mixed>> $orderInformation
136
     * @return array<string, array<string, mixed>>
137
     */
138
    protected function setProviderOrder(array $orderInformation): array
139
    {
140
        foreach ($orderInformation as $provider => &$configuration) {
×
141
            if (isset($configuration['before'])) {
×
142
                $configuration['before'] = $this->getOrderConfiguration($provider, $configuration, 'before');
×
143
            }
144
            if (isset($configuration['after'])) {
×
145
                $configuration['after'] = $this->getOrderConfiguration($provider, $configuration, 'after');
×
146
            }
147
        }
148
        return $orderInformation;
×
149
    }
150

151
    /**
152
     * @param array<string, mixed> $configuration
153
     * @return string[]
154
     */
155
    private function getOrderConfiguration(string $provider, array $configuration, string $key): array
156
    {
157
        if (is_string($configuration[$key])) {
×
158
            return GeneralUtility::trimExplode(',', $configuration[$key], true);
×
159
        }
160
        if (!is_array($configuration[$key])) {
×
161
            throw new \UnexpectedValueException(
×
162
                'The specified "' . $key . '" order configuration for provider "' . $provider . '" is invalid.',
×
163
                1551014599
×
164
            );
×
165
        }
166
        return $configuration[$key];
×
167
    }
168

169
    protected function getSourceComment(): string
170
    {
171
        return '<!-- This site is optimized with the Yoast SEO for TYPO3 plugin - https://yoast.com/typo3-extensions-seo/ -->';
×
172
    }
173
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc