Loader::renderPageContent()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 10
ccs 0
cts 8
cp 0
crap 6
rs 10
1
<?php
2
3
namespace Aoe\Restler\System\TYPO3;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2024 AOE GmbH <[email protected]>
9
 *
10
 *  All rights reserved
11
 *
12
 *  This script is part of the TYPO3 project. The TYPO3 project is
13
 *  free software; you can redistribute it and/or modify
14
 *  it under the terms of the GNU General Public License as published by
15
 *  the Free Software Foundation; either version 3 of the License, or
16
 *  (at your option) any later version.
17
 *
18
 *  The GNU General Public License can be found at
19
 *  http://www.gnu.org/copyleft/gpl.html.
20
 *
21
 *  This script is distributed in the hope that it will be useful,
22
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 *  GNU General Public License for more details.
25
 *
26
 *  This copyright notice MUST APPEAR in all copies of the script!
27
 ***************************************************************/
28
29
use LogicException;
30
use Psr\Http\Message\ServerRequestInterface;
31
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
32
use TYPO3\CMS\Core\Context\TypoScriptAspect;
33
use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
34
use TYPO3\CMS\Core\Http\NormalizedParams;
35
use TYPO3\CMS\Core\Http\Response;
36
use TYPO3\CMS\Core\Routing\PageArguments;
37
use TYPO3\CMS\Core\SingletonInterface;
38
use TYPO3\CMS\Core\Site\Entity\Site;
39
use TYPO3\CMS\Core\Site\SiteFinder;
40
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
41
use TYPO3\CMS\Core\TypoScript\FrontendTypoScript;
42
use TYPO3\CMS\Core\Utility\GeneralUtility;
43
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
44
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
45
use TYPO3\CMS\Frontend\Http\RequestHandler;
46
use TYPO3\CMS\Frontend\Middleware\BackendUserAuthenticator;
47
use TYPO3\CMS\Frontend\Middleware\FrontendUserAuthenticator;
48
use TYPO3\CMS\Frontend\Middleware\PrepareTypoScriptFrontendRendering;
49
use TYPO3\CMS\Frontend\Middleware\TypoScriptFrontendInitialization;
50
51
class Loader implements SingletonInterface
52
{
53
    public function __construct(
54
        private readonly BackendUserAuthenticator $backendUserAuthenticator,
55
        private readonly FrontendUserAuthenticator $frontendUserAuthenticator,
56
        private readonly MockRequestHandler $mockRequestHandler,
57
        private readonly RequestHandler $requestHandler,
58
        private readonly TimeTracker $timeTracker,
59
        private readonly TypoScriptFrontendInitialization $typoScriptFrontendInitialization
60
    ) {
61
    }
62
63
    /**
64
     * Initialize backend-user with BackendUserAuthenticator middleware.
65
     * @see \TYPO3\CMS\Frontend\Middleware\BackendUserAuthenticator
66
     */
67
    public function initializeBackendUser(): void
68
    {
69
        if ($this->hasActiveBackendUser()) {
70
            // Backend-User is already initialized - this can happen when we use/call internal REST-endpoints inside of a normal TYPO3-page
71
            return;
72
        }
73
74
        $this->backendUserAuthenticator->process($this->getRequest(), $this->mockRequestHandler);
75
        self::setRequest($this->mockRequestHandler->getRequest());
76
    }
77
78
    /**
79
     * Checks if a backend user is logged in.
80
     */
81
    public function hasActiveBackendUser(): bool
82
    {
83
        return ($GLOBALS['BE_USER'] ?? null) instanceof BackendUserAuthentication &&
84
            $GLOBALS['BE_USER']->user['uid'] > 0;
85
    }
86
87
    public function getBackendUser(): BackendUserAuthentication
88
    {
89
        if (!$this->hasActiveBackendUser()) {
90
            throw new LogicException("be-user is not initialized - initialize with BE-user with method 'initializeBackendUser'");
91
        }
92
93
        return $GLOBALS['BE_USER'];
94
    }
95
96
    /**
97
     * Initialize frontend-user with FrontendUserAuthenticator middleware.
98
     * @param string|int $pid List of page IDs (comma separated) or page ID where to look for frontend user records
99
     * @see \TYPO3\CMS\Frontend\Middleware\FrontendUserAuthenticator
100
     */
101
    public function initializeFrontendUser($pid = 0): void
102
    {
103
        if ($this->hasActiveFrontendUser()) {
104
            // Frontend-User is already initialized - this can happen when we use/call internal REST-endpoints inside of a normal TYPO3-page
105
            return;
106
        }
107
108
        /** @var ServerRequestInterface $request */
109
        $request = $this->getRequest()
110
            ->withQueryParams(array_merge($_GET, ['pid' => $pid]))
111
            ->withCookieParams($_COOKIE);
112
113
        $this->frontendUserAuthenticator->process($request, $this->mockRequestHandler);
114
        self::setRequest($this->mockRequestHandler->getRequest());
115
    }
116
117
    /**
118
     * Checks if a frontend user is logged in and the session is active.
119
     */
120
    public function hasActiveFrontendUser(): bool
121
    {
122
        $frontendUser = $this->getRequest()
123
            ->getAttribute('frontend.user');
124
        return $frontendUser instanceof FrontendUserAuthentication && is_array($frontendUser->user) && isset($frontendUser->user['uid']);
125
    }
126
127
    public function getFrontendUser(): FrontendUserAuthentication
128
    {
129
        if (!$this->hasActiveFrontendUser()) {
130
            throw new LogicException('fe-user is not initialized');
131
        }
132
133
        return $this->getRequest()
134
            ->getAttribute('frontend.user');
135
    }
136
137
    public function initializeFrontendRendering(int $pageId = 0, int $type = 0, bool $forcedTemplateParsing = true): void
138
    {
139
        if ($this->isFrontendInitialized()) {
140
            // FE is already initialized - this can happen when we use/call internal REST-endpoints inside of a normal TYPO3-page
141
            return;
142
        }
143
144
        /** @var SiteFinder $siteFinder */
145
        $siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
146
        /** @var Site $site */
147
        $site = $siteFinder->getSiteByPageId($pageId);
148
        $pageArguments = new PageArguments($pageId, (string) $type, [], [], []);
149
        $normalizedParams = NormalizedParams::createFromRequest($this->getRequest());
150
151
        /** @var ServerRequestInterface $request */
152
        $request = $this->getRequest()
153
            ->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE)
154
            ->withAttribute('site', $site)
155
            ->withAttribute('routing', $pageArguments)
156
            ->withAttribute('language', $site->getDefaultLanguage())
157
            ->withAttribute('normalizedParams', $normalizedParams)
158
            ->withQueryParams($_GET)
159
            ->withCookieParams($_COOKIE);
160
        self::setRequest($request);
161
162
        $this->initializeBackendUser();
163
        $this->initializeFrontendUser();
164
165
        $this->typoScriptFrontendInitialization->process($this->getRequest(), $this->mockRequestHandler);
166
        self::setRequest($this->mockRequestHandler->getRequest());
167
168
        if ($forcedTemplateParsing) {
169
            // Force TemplateParsing (will slow down the called REST-endpoint a little bit):
170
            // Otherwise we can't render TYPO3-content in REST-endpoints, when TYPO3-cache 'pages' already exists
171
            /** @var TypoScriptFrontendController $controller */
172
            $controller = $this->getRequest()
173
                ->getAttribute('frontend.controller');
174
            $controller->getContext()
175
                ->setAspect('typoscript', new TypoScriptAspect($forcedTemplateParsing));
176
        }
177
178
        $prepareTypoScriptFrontendRendering = new PrepareTypoScriptFrontendRendering($this->timeTracker);
179
        $prepareTypoScriptFrontendRendering->process($this->getRequest(), $this->mockRequestHandler);
180
        self::setRequest($this->mockRequestHandler->getRequest());
181
    }
182
183
    public function renderPageContent(): string
184
    {
185
        if (!$this->isFrontendInitialized()) {
186
            throw new LogicException("FrontendRendering is not initialized - initialize with method 'initializeFrontendRendering'");
187
        }
188
189
        /** @var Response $response */
190
        $response = $this->requestHandler->handle($this->getRequest());
191
        return $response->getBody()
192
            ->__toString();
193
    }
194
195
    public static function setRequest(ServerRequestInterface $request): void
196
    {
197
        $GLOBALS['TYPO3_REQUEST'] = $request;
198
    }
199
200
    private function getRequest(): ServerRequestInterface
201
    {
202
        return $GLOBALS['TYPO3_REQUEST'];
203
    }
204
205
    private function isFrontendInitialized(): bool
206
    {
207
        return ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface &&
208
            $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.controller') instanceof TypoScriptFrontendController &&
209
            $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.typoscript') instanceof FrontendTypoScript;
210
    }
211
}
212