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

FluidTYPO3 / flux / 27757675993

18 Jun 2026 11:55AM UTC coverage: 89.162% (-3.5%) from 92.646%
27757675993

push

github

NamelessCoder
[TASK] Address last phpstan warnings

6228 of 6985 relevant lines covered (89.16%)

40.84 hits per line

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

84.43
/Classes/Integration/Configuration/SpooledConfigurationApplicator.php
1
<?php
2
namespace FluidTYPO3\Flux\Integration\Configuration;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use Doctrine\DBAL\Exception\TableNotFoundException;
12
use FluidTYPO3\Flux\Builder\ContentTypeBuilder;
13
use FluidTYPO3\Flux\Builder\RequestBuilder;
14
use FluidTYPO3\Flux\Content\ContentTypeManager;
15
use FluidTYPO3\Flux\Content\TypeDefinition\FluidRenderingContentTypeDefinitionInterface;
16
use FluidTYPO3\Flux\Core;
17
use FluidTYPO3\Flux\Enum\FormOption;
18
use FluidTYPO3\Flux\Form;
19
use FluidTYPO3\Flux\Provider\Provider;
20
use FluidTYPO3\Flux\Provider\ProviderInterface;
21
use FluidTYPO3\Flux\Service\CacheService;
22
use FluidTYPO3\Flux\Utility\ExtensionNamingUtility;
23
use FluidTYPO3\Flux\Utility\VersionUtility;
24
use Symfony\Component\Finder\Finder;
25
use TYPO3\CMS\Core\Core\ApplicationContext;
26
use TYPO3\CMS\Core\Core\Environment;
27
use TYPO3\CMS\Core\Exception\Page\PageNotFoundException;
28
use TYPO3\CMS\Core\Package\PackageManager;
29
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
31
use TYPO3Fluid\Fluid\Exception;
32

33
class SpooledConfigurationApplicator
34
{
35
    private const string CACHE_ID_SORTINGS = 'flux_contentType_sortingValues';
36

37
    private ContentTypeBuilder $contentTypeBuilder;
38
    private ContentTypeManager $contentTypeManager;
39
    private RequestBuilder $requestBuilder;
40
    private PackageManager $packageManager;
41
    private CacheService $cacheService;
42

43
    public function __construct(
44
        ContentTypeBuilder $contentTypeBuilder,
45
        ContentTypeManager $contentTypeManager,
46
        RequestBuilder $requestBuilder,
47
        PackageManager $packageManager,
48
        CacheService $cacheService
49
    ) {
50
        $this->contentTypeBuilder = $contentTypeBuilder;
24✔
51
        $this->contentTypeManager = $contentTypeManager;
24✔
52
        $this->requestBuilder = $requestBuilder;
24✔
53
        $this->packageManager = $packageManager;
24✔
54
        $this->cacheService = $cacheService;
24✔
55
    }
56

57
    public function processData(): void
58
    {
59
        // Initialize the TCA needed by "template as CType" integrations
60
        $this->spoolQueuedContentTypeTableConfigurations(Core::getQueuedContentTypeRegistrations());
20✔
61

62
        foreach ($this->contentTypeManager->fetchContentTypes() as $contentType) {
20✔
63
            if (!$contentType instanceof FluidRenderingContentTypeDefinitionInterface) {
20✔
64
                continue;
×
65
            }
66
            Core::registerTemplateAsContentType(
20✔
67
                $contentType->getExtensionIdentity(),
20✔
68
                $contentType->getTemplatePathAndFilename(),
20✔
69
                $contentType->getContentTypeName(),
20✔
70
                $contentType->getProviderClassName()
20✔
71
            );
20✔
72
        }
73

74
        $this->spoolQueuedContentTypeRegistrations(Core::getQueuedContentTypeRegistrations());
20✔
75
        Core::clearQueuedContentTypeRegistrations();
12✔
76

77
        $scopedRequire = static function (string $filename): void {
12✔
78
            require $filename;
×
79
        };
12✔
80

81
        $activePackages = $this->packageManager->getActivePackages();
12✔
82
        foreach ($activePackages as $package) {
12✔
83
            try {
84
                $finder = Finder::create()
×
85
                    ->files()
×
86
                    ->sortByName()
×
87
                    ->depth(0)
×
88
                    ->name('*.php')
×
89
                    ->in($package->getPackagePath() . 'Configuration/TCA/Flux');
×
90
            } catch (\InvalidArgumentException $e) {
×
91
                // No such directory in this package
92
                continue;
×
93
            }
94
            foreach ($finder as $fileInfo) {
×
95
                $scopedRequire($fileInfo->getPathname());
×
96
            }
97
        }
98

99
        if (VersionUtility::isCoreAtLeast13()) {
12✔
100
            // Fix TYPO3 13+ loading order of TCA for schemaFactory, flux does its TCA stuff too late, so we rebuild
101
            // the tcaSchema after flux has done its work.
102
            $tcaSchemaFactory = GeneralUtility::makeInstance(TcaSchemaFactory::class);
9✔
103
            $tcaSchemaFactory->rebuild($GLOBALS['TCA']);
9✔
104
        }
105
    }
106

107
    private function spoolQueuedContentTypeTableConfigurations(array $queue): void
108
    {
109
        foreach ($queue as $queuedRegistration) {
24✔
110
            [$extensionName, $templatePathAndFilename, , $contentType] = $queuedRegistration;
12✔
111
            $contentType = $contentType ?: $this->determineContentType($extensionName, $templatePathAndFilename);
12✔
112
            $this->contentTypeBuilder->addBoilerplateTableConfiguration($contentType);
12✔
113
        }
114
    }
115

116
    private function determineContentType(
117
        string $providerExtensionName,
118
        string $templatePathAndFilename
119
    ): string {
120
        // Determine which plugin name and controller action to emulate with this CType, base on file name.
121
        $controllerExtensionName = $providerExtensionName;
20✔
122
        $emulatedPluginName = ucfirst(pathinfo($templatePathAndFilename, PATHINFO_FILENAME));
20✔
123
        $extensionSignature = str_replace('_', '', ExtensionNamingUtility::getExtensionKey($controllerExtensionName));
20✔
124
        $fullContentType = $extensionSignature . '_' . strtolower($emulatedPluginName);
20✔
125
        return $fullContentType;
20✔
126
    }
127

128
    protected function spoolQueuedContentTypeRegistrations(array $queue): void
129
    {
130
        $applicationContext = $this->getApplicationContext();
20✔
131
        $providers = [];
20✔
132
        foreach ($queue as $queuedRegistration) {
20✔
133
            [
20✔
134
                $providerExtensionName,
20✔
135
                $templateFilename,
20✔
136
                $providerClassName,
20✔
137
                $contentType,
20✔
138
                $pluginName,
20✔
139
                $controllerActionName
20✔
140
            ] = $queuedRegistration;
20✔
141
            try {
142
                $contentType = $contentType ?: $this->determineContentType($providerExtensionName, $templateFilename);
20✔
143
                $defaultControllerExtensionName = 'FluidTYPO3.Flux';
20✔
144

145
                /** @var ProviderInterface $provider */
146
                $provider = $this->contentTypeBuilder->configureContentTypeFromTemplateFile(
20✔
147
                    $providerExtensionName,
20✔
148
                    $templateFilename,
20✔
149
                    $providerClassName ?? Provider::class,
20✔
150
                    $contentType,
20✔
151
                    $defaultControllerExtensionName,
20✔
152
                    $controllerActionName
20✔
153
                );
20✔
154

155
                $provider->setPluginName($pluginName);
12✔
156

157
                Core::registerConfigurationProvider($provider);
12✔
158

159
                $providers[] = $provider;
12✔
160
            } catch (Exception $error) {
8✔
161
                if (!$applicationContext->isProduction()) {
8✔
162
                    throw $error;
4✔
163
                }
164
            }
165
        }
166

167
        $backup = $GLOBALS['TYPO3_REQUEST'] ?? null;
16✔
168

169
        $GLOBALS['TYPO3_REQUEST'] = $this->requestBuilder->getServerRequest();
16✔
170

171
        $sortingValues = $this->resolveSortingValues($providers);
16✔
172
        uasort(
16✔
173
            $providers,
16✔
174
            function (ProviderInterface $item1, ProviderInterface $item2) use ($sortingValues) {
16✔
175
                $contentType1 = $item1->getContentObjectType();
12✔
176
                $contentType2 = $item2->getContentObjectType();
12✔
177
                return ($sortingValues[$contentType1] ?? 0) <=> ($sortingValues[$contentType2] ?? 0);
12✔
178
            }
16✔
179
        );
16✔
180

181
        $providerExtensionNamesToFlush = [];
16✔
182

183
        foreach ($providers as $provider) {
16✔
184
            $contentType = $provider->getContentObjectType();
12✔
185
            $virtualRecord = ['CType' => $contentType];
12✔
186
            $providerExtensionName = $provider->getExtensionKey($virtualRecord);
12✔
187

188
            $providerExtensionNamesToFlush[] = $providerExtensionName;
12✔
189

190
            try {
191
                $this->contentTypeBuilder->registerContentType($providerExtensionName, $contentType, $provider);
12✔
192
            } catch (PageNotFoundException|TableNotFoundException $error) {
8✔
193
                // Suppressed: Flux bootstrap does not care if a page can be resolved or not.
194
            } catch (Exception $error) {
8✔
195
                if (!$applicationContext->isProduction()) {
8✔
196
                    $GLOBALS['TYPO3_REQUEST'] = $backup;
4✔
197
                    throw $error;
4✔
198
                }
199
            }
200
        }
201

202
        // Flush the cache entry that was generated; make sure any TypoScript overrides will take place once
203
        // all TypoScript is finally loaded.
204
        foreach (array_unique($providerExtensionNamesToFlush) as $providerExtensionName) {
12✔
205
            $this->cacheService->remove(
8✔
206
                'viewpaths_' . ExtensionNamingUtility::getExtensionKey($providerExtensionName)
8✔
207
            );
8✔
208
        }
209

210
        $GLOBALS['TYPO3_REQUEST'] = $backup;
12✔
211
    }
212

213
    /**
214
     * @param ProviderInterface[] $providers
215
     */
216
    private function resolveSortingValues(array $providers): array
217
    {
218
        /** @var array|false $fromCache */
219
        $fromCache = $this->cacheService->getFromCaches(self::CACHE_ID_SORTINGS);
16✔
220
        if ($fromCache) {
16✔
221
            return $fromCache;
×
222
        }
223

224
        $store = true;
16✔
225
        $sortingValues = [];
16✔
226
        foreach ($providers as $provider) {
16✔
227
            $contentObjectType = $provider->getContentObjectType();
12✔
228
            try {
229
                $form = $provider->getForm(['CType' => $contentObjectType]);
12✔
230
                $sortingValues[$contentObjectType] = $this->resolveSortingValue($form);
12✔
231
            } catch (\Exception $exception) {
×
232
                // A raised exception is ignored and causes sorting value to be "0", but disables storing to cache.
233
                $store = false;
×
234
            }
235
        }
236

237
        if ($store) {
16✔
238
            $this->cacheService->setInCaches($sortingValues, true, self::CACHE_ID_SORTINGS);
16✔
239
        }
240

241
        return $sortingValues;
16✔
242
    }
243

244
    private function resolveSortingValue(?Form $form): string
245
    {
246
        $sortingOptionValue = 0;
12✔
247
        if ($form instanceof Form\FormInterface) {
12✔
248
            if ($form->hasOption(FormOption::SORTING)) {
12✔
249
                $sortingOptionValue = $form->getOption(FormOption::SORTING);
12✔
250
            } elseif ($form->hasOption(FormOption::TEMPLATE_FILE)) {
×
251
                /** @var string $templateFilename */
252
                $templateFilename = $form->getOption(FormOption::TEMPLATE_FILE);
×
253
                $sortingOptionValue = basename($templateFilename);
×
254
            } else {
255
                $sortingOptionValue = $form->getId();
×
256
            }
257
        }
258
        return !is_scalar($sortingOptionValue) ? '0' : (string) $sortingOptionValue;
12✔
259
    }
260

261
    /**
262
     * @codeCoverageIgnore
263
     */
264
    protected function getApplicationContext(): ApplicationContext
265
    {
266
        return Environment::getContext();
267
    }
268
}
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