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

FluidTYPO3 / flux / 8984651213

07 May 2024 11:37AM UTC coverage: 93.421% (-0.09%) from 93.508%
8984651213

push

github

NamelessCoder
[TER] 10.0.10

6802 of 7281 relevant lines covered (93.42%)

46.26 hits per line

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

84.03
/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 Symfony\Component\Finder\Finder;
24
use TYPO3\CMS\Core\Core\ApplicationContext;
25
use TYPO3\CMS\Core\Core\Environment;
26
use TYPO3\CMS\Core\Exception\Page\PageNotFoundException;
27
use TYPO3\CMS\Core\Package\PackageManager;
28
use TYPO3Fluid\Fluid\Exception;
29

30
class SpooledConfigurationApplicator
31
{
32
    private const CACHE_ID_SORTINGS = 'flux_contentType_sortingValues';
33

34
    private ContentTypeBuilder $contentTypeBuilder;
35
    private ContentTypeManager $contentTypeManager;
36
    private RequestBuilder $requestBuilder;
37
    private PackageManager $packageManager;
38
    private CacheService $cacheService;
39

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

54
    public function processData(): void
55
    {
56
        // Initialize the TCA needed by "template as CType" integrations
57
        $this->spoolQueuedContentTypeTableConfigurations(Core::getQueuedContentTypeRegistrations());
25✔
58

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

71
        $this->spoolQueuedContentTypeRegistrations(Core::getQueuedContentTypeRegistrations());
25✔
72
        Core::clearQueuedContentTypeRegistrations();
15✔
73

74
        $scopedRequire = static function (string $filename): void {
15✔
75
            require $filename;
×
76
        };
15✔
77

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

97
    private function spoolQueuedContentTypeTableConfigurations(array $queue): void
98
    {
99
        foreach ($queue as $queuedRegistration) {
30✔
100
            [$extensionName, $templatePathAndFilename, , $contentType] = $queuedRegistration;
15✔
101
            $contentType = $contentType ?: $this->determineContentType($extensionName, $templatePathAndFilename);
15✔
102
            $this->contentTypeBuilder->addBoilerplateTableConfiguration($contentType);
15✔
103
        }
104
    }
105

106
    private function determineContentType(
107
        string $providerExtensionName,
108
        string $templatePathAndFilename
109
    ): string {
110
        // Determine which plugin name and controller action to emulate with this CType, base on file name.
111
        $controllerExtensionName = $providerExtensionName;
25✔
112
        $emulatedPluginName = ucfirst(pathinfo($templatePathAndFilename, PATHINFO_FILENAME));
25✔
113
        $extensionSignature = str_replace('_', '', ExtensionNamingUtility::getExtensionKey($controllerExtensionName));
25✔
114
        $fullContentType = $extensionSignature . '_' . strtolower($emulatedPluginName);
25✔
115
        return $fullContentType;
25✔
116
    }
117

118
    protected function spoolQueuedContentTypeRegistrations(array $queue): void
119
    {
120
        $applicationContext = $this->getApplicationContext();
25✔
121
        $providers = [];
25✔
122
        foreach ($queue as $queuedRegistration) {
25✔
123
            [
25✔
124
                $providerExtensionName,
25✔
125
                $templateFilename,
25✔
126
                $providerClassName,
25✔
127
                $contentType,
25✔
128
                $pluginName,
25✔
129
                $controllerActionName
25✔
130
            ] = $queuedRegistration;
25✔
131
            try {
132
                $contentType = $contentType ?: $this->determineContentType($providerExtensionName, $templateFilename);
25✔
133
                $defaultControllerExtensionName = 'FluidTYPO3.Flux';
25✔
134

135
                /** @var ProviderInterface $provider */
136
                $provider = $this->contentTypeBuilder->configureContentTypeFromTemplateFile(
25✔
137
                    $providerExtensionName,
25✔
138
                    $templateFilename,
25✔
139
                    $providerClassName ?? Provider::class,
25✔
140
                    $contentType,
25✔
141
                    $defaultControllerExtensionName,
25✔
142
                    $controllerActionName
25✔
143
                );
25✔
144

145
                $provider->setPluginName($pluginName);
15✔
146

147
                Core::registerConfigurationProvider($provider);
15✔
148

149
                $providers[] = $provider;
15✔
150
            } catch (Exception $error) {
10✔
151
                if (!$applicationContext->isProduction()) {
10✔
152
                    throw $error;
5✔
153
                }
154
            }
155
        }
156

157
        $backup = $GLOBALS['TYPO3_REQUEST'] ?? null;
20✔
158

159
        $GLOBALS['TYPO3_REQUEST'] = $this->requestBuilder->getServerRequest();
20✔
160

161
        $sortingValues = $this->resolveSortingValues($providers);
20✔
162
        uasort(
20✔
163
            $providers,
20✔
164
            function (ProviderInterface $item1, ProviderInterface $item2) use ($sortingValues) {
20✔
165
                $contentType1 = $item1->getContentObjectType();
15✔
166
                $contentType2 = $item2->getContentObjectType();
15✔
167
                return ($sortingValues[$contentType1] ?? 0) <=> ($sortingValues[$contentType2] ?? 0);
15✔
168
            }
20✔
169
        );
20✔
170

171
        $providerExtensionNamesToFlush = [];
20✔
172

173
        foreach ($providers as $provider) {
20✔
174
            $contentType = $provider->getContentObjectType();
15✔
175
            $virtualRecord = ['CType' => $contentType];
15✔
176
            $providerExtensionName = $provider->getExtensionKey($virtualRecord);
15✔
177

178
            $providerExtensionNamesToFlush[] = $providerExtensionName;
15✔
179

180
            try {
181
                $this->contentTypeBuilder->registerContentType($providerExtensionName, $contentType, $provider);
15✔
182
            } catch (PageNotFoundException|TableNotFoundException $error) {
10✔
183
                // Suppressed: Flux bootstrap does not care if a page can be resolved or not.
184
            } catch (Exception $error) {
10✔
185
                if (!$applicationContext->isProduction()) {
10✔
186
                    $GLOBALS['TYPO3_REQUEST'] = $backup;
5✔
187
                    throw $error;
5✔
188
                }
189
            }
190
        }
191

192
        // Flush the cache entry that was generated; make sure any TypoScript overrides will take place once
193
        // all TypoScript is finally loaded.
194
        foreach (array_unique($providerExtensionNamesToFlush) as $providerExtensionName) {
15✔
195
            $this->cacheService->remove(
10✔
196
                'viewpaths_' . ExtensionNamingUtility::getExtensionKey($providerExtensionName)
10✔
197
            );
10✔
198
        }
199

200
        $GLOBALS['TYPO3_REQUEST'] = $backup;
15✔
201
    }
202

203
    /**
204
     * @param ProviderInterface[] $providers
205
     */
206
    private function resolveSortingValues(array $providers): array
207
    {
208
        /** @var array|false $fromCache */
209
        $fromCache = $this->cacheService->getFromCaches(self::CACHE_ID_SORTINGS);
20✔
210
        if ($fromCache) {
20✔
211
            return $fromCache;
×
212
        }
213

214
        $store = true;
20✔
215
        $sortingValues = [];
20✔
216
        foreach ($providers as $provider) {
20✔
217
            $contentObjectType = $provider->getContentObjectType();
15✔
218
            try {
219
                $form = $provider->getForm(['CType' => $contentObjectType]);
15✔
220
                $sortingValues[$contentObjectType] = $this->resolveSortingValue($form);
15✔
221
            } catch (\Exception $exception) {
×
222
                // A raised exception is ignored and causes sorting value to be "0", but disables storing to cache.
223
                $store = false;
×
224
            }
225
        }
226

227
        if ($store) {
20✔
228
            $this->cacheService->setInCaches($sortingValues, true, self::CACHE_ID_SORTINGS);
20✔
229
        }
230

231
        return $sortingValues;
20✔
232
    }
233

234
    private function resolveSortingValue(?Form $form): string
235
    {
236
        $sortingOptionValue = 0;
15✔
237
        if ($form instanceof Form\FormInterface) {
15✔
238
            if ($form->hasOption(FormOption::SORTING)) {
15✔
239
                $sortingOptionValue = $form->getOption(FormOption::SORTING);
15✔
240
            } elseif ($form->hasOption(FormOption::TEMPLATE_FILE)) {
×
241
                /** @var string $templateFilename */
242
                $templateFilename = $form->getOption(FormOption::TEMPLATE_FILE);
×
243
                $sortingOptionValue = basename($templateFilename);
×
244
            } else {
245
                $sortingOptionValue = $form->getId();
×
246
            }
247
        }
248
        return !is_scalar($sortingOptionValue) ? '0' : (string) $sortingOptionValue;
15✔
249
    }
250

251
    /**
252
     * @codeCoverageIgnore
253
     */
254
    protected function getApplicationContext(): ApplicationContext
255
    {
256
        return Environment::getContext();
257
    }
258
}
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