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

Yoast / Yoast-SEO-for-TYPO3 / 13327579701

14 Feb 2025 10:43AM UTC coverage: 1.276%. First build
13327579701

push

github

web-flow
Merge pull request #597 from Yoast/feature/v11

[FEATURE] Release 11.0.0

21 of 894 new or added lines in 76 files covered. (2.35%)

35 of 2744 relevant lines covered (1.28%)

0.04 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
declare(strict_types=1);
4

5
namespace YoastSeoForTypo3\YoastSeo\StructuredData;
6

7
use Psr\Http\Message\ServerRequestInterface;
8
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
9
use TYPO3\CMS\Core\Context\Context;
10
use TYPO3\CMS\Core\Http\ApplicationType;
11
use TYPO3\CMS\Core\Information\Typo3Version;
12
use TYPO3\CMS\Core\Service\DependencyOrderingService;
13
use TYPO3\CMS\Core\SingletonInterface;
14
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
15
use TYPO3\CMS\Core\Utility\GeneralUtility;
16
use TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator;
17
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
18

19
/**
20
 * This class will take care of the different providers and returns the title with
21
 * the highest priority
22
 */
23
class StructuredDataProviderManager implements SingletonInterface
24
{
25
    public function __construct(
26
        protected FrontendInterface $pageCache
27
    ) {}
×
28

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

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

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

44
    /**
45
     * @param array<string, array<string, mixed>> $src
46
     */
47
    protected function buildJsonLdBlob(array $src): string
48
    {
49
        $data = [];
×
50
        foreach ($src as $dataItems) {
×
51
            foreach ($dataItems as $item) {
×
52
                $data[] = $item;
×
53
            }
54
        }
55

56
        if (empty($data)) {
×
57
            return '';
×
58
        }
59

60
        return '<script type="application/ld+json">' . json_encode($data) . '</script>';
×
61
    }
62

63
    /**
64
     * @return array<string, array<string, mixed>>
65
     */
66
    public function getStructuredData(): array
67
    {
68
        $structuredData = [];
×
69
        foreach ($this->getOrderedStructuredDataProviders() as $provider => $configuration) {
×
70
            $cacheIdentifier = $this->getTypoScriptFrontendController()->newHash . '-structured-data-' . $provider;
×
71
            if ($this->pageCache instanceof FrontendInterface) {
×
72
                $data = $this->pageCache->get($cacheIdentifier);
×
73
                if ($data !== false) {
×
74
                    $structuredData[$provider] = $data;
×
75
                    continue;
×
76
                }
77
            }
78
            $structuredDataProviderObject = $this->getStructuredDataProviderObject($configuration);
×
79
            if ($structuredDataProviderObject === null) {
×
80
                continue;
×
81
            }
82

83
            if ($data = $structuredDataProviderObject->getData()) {
×
84
                $this->pageCache->set(
×
85
                    $cacheIdentifier,
×
86
                    $data,
×
87
                    ['pageId_' . ($this->getTypoScriptFrontendController()->page['uid'] ?? $this->getTypoScriptFrontendController()->id)],
×
NEW
88
                    $this->getCacheTimeout(),
×
89
                );
×
90
            }
91

92
            if (!empty($data)) {
×
93
                $structuredData[$provider] = $data;
×
94
            }
95
        }
96

97
        return $structuredData;
×
98
    }
99

100
    /**
101
     * @param array<string, mixed> $configuration
102
     */
103
    protected function getStructuredDataProviderObject(array $configuration): StructuredDataProviderInterface|null
104
    {
105
        if (!class_exists($configuration['provider']) || !is_subclass_of($configuration['provider'], StructuredDataProviderInterface::class)) {
×
106
            return null;
×
107
        }
108

109
        $providerObject = GeneralUtility::makeInstance($configuration['provider']);
×
110
        if (method_exists($providerObject, 'setConfiguration')) {
×
111
            $providerObject->setConfiguration($configuration);
×
112
        }
113
        return $providerObject;
×
114
    }
115

116
    private function getTypoScriptFrontendController(): TypoScriptFrontendController
117
    {
118
        return $GLOBALS['TSFE'];
×
119
    }
120

121
    /**
122
     * @return array<string, array<string, mixed>>
123
     */
124
    private function getOrderedStructuredDataProviders(): array
125
    {
126
        $structuredDataProviders = $this->getStructuredDataProviderConfiguration();
×
127
        $structuredDataProviders = $this->setProviderOrder($structuredDataProviders);
×
128

129
        return GeneralUtility::makeInstance(DependencyOrderingService::class)
×
130
            ->orderByDependencies($structuredDataProviders);
×
131
    }
132

133
    /**
134
     * @return array<string, array<string, mixed>>
135
     */
136
    private function getStructuredDataProviderConfiguration(): array
137
    {
138
        $typoscriptService = GeneralUtility::makeInstance(TypoScriptService::class);
×
139
        $config = $typoscriptService->convertTypoScriptArrayToPlainArray(
×
140
            $this->getTypoScriptFrontendController()->config['config'] ?? []
×
141
        );
×
142

143
        return $config['structuredData']['providers'] ?? [];
×
144
    }
145

146
    /**
147
     * @param array<string, array<string, mixed>> $orderInformation
148
     * @return array<string, array<string, mixed>>
149
     */
150
    protected function setProviderOrder(array $orderInformation): array
151
    {
152
        foreach ($orderInformation as $provider => &$configuration) {
×
153
            if (isset($configuration['before'])) {
×
154
                $configuration['before'] = $this->getOrderConfiguration($provider, $configuration, 'before');
×
155
            }
156
            if (isset($configuration['after'])) {
×
157
                $configuration['after'] = $this->getOrderConfiguration($provider, $configuration, 'after');
×
158
            }
159
        }
160
        return $orderInformation;
×
161
    }
162

163
    /**
164
     * @param array<string, mixed> $configuration
165
     * @return string[]
166
     */
167
    private function getOrderConfiguration(string $provider, array $configuration, string $key): array
168
    {
169
        if (is_string($configuration[$key])) {
×
170
            return GeneralUtility::trimExplode(',', $configuration[$key], true);
×
171
        }
172
        if (!is_array($configuration[$key])) {
×
173
            throw new \UnexpectedValueException(
×
174
                'The specified "' . $key . '" order configuration for provider "' . $provider . '" is invalid.',
×
175
                1551014599
×
176
            );
×
177
        }
178
        return $configuration[$key];
×
179
    }
180

181
    protected function isFrontendRequest(): bool
182
    {
183
        return ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
×
184
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend();
×
185
    }
186

187
    protected function getSourceComment(): string
188
    {
189
        return '<!-- This site is optimized with the Yoast SEO for TYPO3 plugin - https://yoast.com/typo3-extensions-seo/ -->';
×
190
    }
191

192
    protected function getCacheTimeout(): int
193
    {
NEW
194
        if (GeneralUtility::makeInstance(Typo3Version::class)->getMajorVersion() < 13) {
×
NEW
195
            return $this->getTypoScriptFrontendController()->get_cache_timeout();
×
196
        }
197

NEW
198
        $request = $GLOBALS['TYPO3_REQUEST'] ?? null;
×
NEW
199
        if ($request === null) {
×
NEW
200
            return 0;
×
201
        }
202

NEW
203
        $pageInformation = $request->getAttribute('frontend.page.information');
×
NEW
204
        $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
×
NEW
205
        $context = GeneralUtility::makeInstance(Context::class);
×
206

NEW
207
        return GeneralUtility::makeInstance(CacheLifetimeCalculator::class)
×
NEW
208
            ->calculateLifetimeForPage(
×
NEW
209
                $pageInformation->getId(),
×
NEW
210
                $pageInformation->getPageRecord(),
×
NEW
211
                $typoScriptConfigArray,
×
NEW
212
                0,
×
NEW
213
                $context
×
NEW
214
            );
×
215
    }
216
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc