Passed
Push — master ( d78354...23b09b )
by
unknown
14:05
created

Bootstrap::convertExtbaseResponseToPsr7Response()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Extbase\Core;
19
20
use Psr\Container\ContainerInterface;
21
use Psr\Http\Message\ResponseInterface;
22
use Psr\Http\Message\ServerRequestInterface;
23
use TYPO3\CMS\Backend\Routing\Route;
24
use TYPO3\CMS\Core\Cache\CacheManager;
25
use TYPO3\CMS\Core\Core\Environment;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
28
use TYPO3\CMS\Extbase\Configuration\RequestHandlersConfigurationFactory;
29
use TYPO3\CMS\Extbase\Mvc\RequestHandlerResolver;
30
use TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder;
31
use TYPO3\CMS\Extbase\Persistence\ClassesConfigurationFactory;
32
use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
33
use TYPO3\CMS\Extbase\Service\CacheService;
34
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
35
36
/**
37
 * Creates a request and dispatches it to the controller which was specified
38
 * by TS Setup, flexForm and returns the content.
39
 *
40
 * This class is the main entry point for extbase extensions.
41
 */
42
class Bootstrap
43
{
44
    /**
45
     * @var array
46
     */
47
    public static $persistenceClasses = [];
48
49
    /**
50
     * Back reference to the parent content object
51
     * This has to be public as it is set directly from TYPO3
52
     *
53
     * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
54
     */
55
    public $cObj;
56
57
    /**
58
     * @var ContainerInterface
59
     */
60
    protected $container;
61
62
    /**
63
     * @var ConfigurationManagerInterface
64
     */
65
    protected $configurationManager;
66
67
    /**
68
     * @var PersistenceManagerInterface
69
     */
70
    protected $persistenceManager;
71
72
    /**
73
     * @var \TYPO3\CMS\Extbase\Mvc\RequestHandlerResolver
74
     */
75
    protected $requestHandlerResolver;
76
77
    /**
78
     * @var \TYPO3\CMS\Extbase\Service\CacheService
79
     */
80
    protected $cacheService;
81
82
    /**
83
     * @var \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder
84
     */
85
    protected $extbaseRequestBuilder;
86
87
    public function __construct(
88
        ContainerInterface $container,
89
        ConfigurationManagerInterface $configurationManager,
90
        PersistenceManagerInterface $persistenceManager,
91
        RequestHandlerResolver $requestHandlerResolver,
92
        CacheService $cacheService,
93
        RequestBuilder $extbaseRequestBuilder
94
    ) {
95
        $this->container = $container;
96
        $this->configurationManager = $configurationManager;
97
        $this->persistenceManager = $persistenceManager;
98
        $this->requestHandlerResolver = $requestHandlerResolver;
99
        $this->cacheService = $cacheService;
100
        $this->extbaseRequestBuilder = $extbaseRequestBuilder;
101
    }
102
103
    /**
104
     * Explicitly initializes all necessary Extbase objects by invoking the various initialize* methods.
105
     *
106
     * Usually this method is only called from unit tests or other applications which need a more fine grained control over
107
     * the initialization and request handling process. Most other applications just call the run() method.
108
     *
109
     * @param array $configuration The TS configuration array
110
     * @throws \RuntimeException
111
     * @see run()
112
     */
113
    public function initialize(array $configuration): void
114
    {
115
        if (!Environment::isCli()) {
116
            if (!isset($configuration['extensionName']) || $configuration['extensionName'] === '') {
117
                throw new \RuntimeException('Invalid configuration: "extensionName" is not set', 1290623020);
118
            }
119
            if (!isset($configuration['pluginName']) || $configuration['pluginName'] === '') {
120
                throw new \RuntimeException('Invalid configuration: "pluginName" is not set', 1290623027);
121
            }
122
        }
123
        $this->initializeConfiguration($configuration);
124
        $this->initializePersistenceClassesConfiguration();
125
        $this->initializeRequestHandlersConfiguration();
126
    }
127
128
    /**
129
     * Initializes the Object framework.
130
     *
131
     * @param array $configuration
132
     * @see initialize()
133
     * @internal
134
     */
135
    public function initializeConfiguration(array $configuration): void
136
    {
137
        /** @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObject */
138
        $contentObject = $this->cObj ?? $this->container->get(ContentObjectRenderer::class);
139
        $this->configurationManager->setContentObject($contentObject);
140
        $this->configurationManager->setConfiguration($configuration);
141
        // todo: Shouldn't the configuration manager object – which is a singleton – be stateless?
142
        // todo: At this point we give the configuration manager a state, while we could directly pass the
143
        // todo: configuration (i.e. controllerName, actionName and such), directly to the request
144
        // todo: handler, which then creates stateful request objects.
145
        // todo: Once this has changed, \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder::loadDefaultValues does not need
146
        // todo: to fetch this configuration from the configuration manager.
147
    }
148
149
    /**
150
     * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
151
     */
152
    private function initializePersistenceClassesConfiguration(): void
153
    {
154
        $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
155
        GeneralUtility::makeInstance(ClassesConfigurationFactory::class, $cacheManager)
156
            ->createClassesConfiguration();
157
    }
158
159
    /**
160
     * @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
161
     */
162
    private function initializeRequestHandlersConfiguration(): void
163
    {
164
        $cacheManager = GeneralUtility::makeInstance(CacheManager::class);
165
        GeneralUtility::makeInstance(RequestHandlersConfigurationFactory::class, $cacheManager)
166
            ->createRequestHandlersConfiguration();
167
    }
168
169
    /**
170
     * Runs the the Extbase Framework by resolving an appropriate Request Handler and passing control to it.
171
     * If the Framework is not initialized yet, it will be initialized.
172
     *
173
     * @param string $content The content. Not used
174
     * @param array $configuration The TS configuration array
175
     * @return string $content The processed content
176
     */
177
    public function run(string $content, array $configuration): string
178
    {
179
        $this->initialize($configuration);
180
        return $this->handleRequest();
181
    }
182
183
    /**
184
     * @return string
185
     */
186
    protected function handleRequest(): string
187
    {
188
        $extbaseRequest = $this->extbaseRequestBuilder->build();
189
        $requestHandler = $this->requestHandlerResolver->resolveRequestHandler($extbaseRequest);
190
        $response = $requestHandler->handleRequest($extbaseRequest);
191
        // If response is NULL after handling the request we need to stop
192
        // This happens for instance, when a USER object was converted to a USER_INT
193
        // @see TYPO3\CMS\Extbase\Mvc\Web\FrontendRequestHandler::handleRequest()
194
        if ($response === null) {
195
            $content = '';
196
        } else {
197
            if (headers_sent() === false) {
198
                foreach ($response->getHeaders() as $name => $values) {
199
                    foreach ($values as $value) {
200
                        header(sprintf('%s: %s', $name, $value));
201
                    }
202
                }
203
            }
204
205
            $body = $response->getBody();
206
            $body->rewind();
207
            $content = $body->getContents();
208
            $this->resetSingletons();
209
            $this->cacheService->clearCachesOfRegisteredPageIds();
210
        }
211
212
        return $content;
213
    }
214
215
    /**
216
     * Entrypoint for backend modules, handling PSR-7 requests/responses
217
     *
218
     * @param ServerRequestInterface $request
219
     * @return ResponseInterface
220
     * @internal
221
     */
222
    public function handleBackendRequest(ServerRequestInterface $request): ResponseInterface
223
    {
224
        // build the configuration from the Server request / route
225
        /** @var Route $route */
226
        $route = $request->getAttribute('route');
227
        $moduleConfiguration = $route->getOption('moduleConfiguration');
228
        $configuration = [
229
            'extensionName' => $moduleConfiguration['extensionName'],
230
            'pluginName' => $route->getOption('moduleName')
231
        ];
232
233
        $this->initialize($configuration);
234
235
        $extbaseRequest = $this->extbaseRequestBuilder->build();
236
        $requestHandler = $this->requestHandlerResolver->resolveRequestHandler($extbaseRequest);
237
        $response = $requestHandler->handleRequest($extbaseRequest);
238
239
        $this->resetSingletons();
240
        $this->cacheService->clearCachesOfRegisteredPageIds();
241
        return $response;
242
    }
243
244
    /**
245
     * Resets global singletons for the next plugin
246
     */
247
    protected function resetSingletons(): void
248
    {
249
        $this->persistenceManager->persistAll();
250
    }
251
}
252