Skip to content

Commit 3703e59

Browse files
committed
Breaking change: Requested language has precedence over Site language
If a language is requested through `$_GET['L']`, `$_GET['locale']`, `$_POST['L']` or HTTP `Accept-Language` header, it will be used if possible. The language from the Site configuration will be used as fallback.
1 parent a901e2e commit 3703e59

File tree

7 files changed

+557
-323
lines changed

7 files changed

+557
-323
lines changed

Classes/Bootstrap.php

Lines changed: 30 additions & 291 deletions
Original file line numberDiff line numberDiff line change
@@ -3,327 +3,66 @@
33

44
namespace Cundd\Rest;
55

6-
use Locale;
7-
use Psr\Http\Message\ServerRequestInterface;
8-
use stdClass;
9-
use TYPO3\CMS\Core\Routing\SiteMatcher;
10-
use TYPO3\CMS\Core\Routing\SiteRouteResult;
11-
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
6+
use Cundd\Rest\Bootstrap\Core;
7+
use Cundd\Rest\Bootstrap\LanguageBootstrap;
8+
use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
9+
use TYPO3\CMS\Core\Http\ServerRequest;
10+
use TYPO3\CMS\Core\Http\ServerRequestFactory;
1211
use TYPO3\CMS\Core\Utility\GeneralUtility;
13-
use TYPO3\CMS\Extbase\Object\ObjectManager;
1412
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
15-
use TYPO3\CMS\Frontend\Utility\EidUtility;
16-
use TYPO3\CMS\Lang\LanguageService;
17-
use function class_exists;
18-
use function intval;
19-
use function is_int;
20-
use function is_string;
21-
2213

2314
/**
24-
* Class to bootstrap TYPO3 frontend controller
15+
* @deprecated will be removed in 5.0. Use \Cundd\Rest\Bootstrap\Core instead
2516
*/
2617
class Bootstrap
2718
{
2819
/**
29-
* Initializes the TYPO3 environment
30-
*
31-
* @param TypoScriptFrontendController|null $frontendController
32-
* @return TypoScriptFrontendController
33-
* @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
34-
* @throws \TYPO3\CMS\Core\Http\ImmediateResponseException
20+
* @var Core
3521
*/
36-
public function init(TypoScriptFrontendController $frontendController = null)
37-
{
38-
$this->initializeTimeTracker();
39-
$this->initializeLanguageObject();
40-
41-
$frontendController = $frontendController ?: $this->buildFrontendController($this->getPageUid());
42-
43-
if ($this->getFrontendControllerIsInitialized()) {
44-
return $GLOBALS['TSFE'];
45-
}
46-
47-
// Register the frontend controller as the global TSFE
48-
$GLOBALS['TSFE'] = $frontendController;
49-
$this->configureFrontendController($frontendController);
50-
51-
return $frontendController;
52-
}
22+
private $coreBootstrap;
5323

5424
/**
55-
* Initialize language object
25+
* @var ServerRequest
5626
*/
57-
public function initializeLanguageObject()
58-
{
59-
if (!isset($GLOBALS['LANG']) || !is_object($GLOBALS['LANG'])) {
60-
/** @var LanguageService $GLOBALS ['LANG'] */
61-
$GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
62-
$GLOBALS['LANG']->init($this->getRequestedLanguageCode());
63-
}
64-
}
65-
66-
private function initializeTimeTracker()
67-
{
68-
if (!isset($GLOBALS['TT']) || !is_object($GLOBALS['TT'])) {
69-
$GLOBALS['TT'] = new TimeTracker();
70-
$GLOBALS['TT']->start();
71-
}
72-
}
73-
74-
/**
75-
* Build the TSFE object
76-
*
77-
* @param int $pageUid
78-
* @return TypoScriptFrontendController
79-
*/
80-
private function buildFrontendController(int $pageUid): TypoScriptFrontendController
81-
{
82-
$cHash = GeneralUtility::_GP('cHash') ?: 'cunddRestFakeHash';
83-
84-
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
85-
86-
return $objectManager->get(
87-
TypoScriptFrontendController::class,
88-
null, // previously TYPO3_CONF_VARS
89-
$pageUid,
90-
0, // Type
91-
0, // no_cache
92-
$cHash, // cHash
93-
null, // previously jumpurl
94-
'', // MP,
95-
'' // RDCT
96-
);
97-
}
98-
99-
/**
100-
* @return int
101-
*/
102-
private function getPageUid(): int
103-
{
104-
return GeneralUtility::_GP('pid') !== null
105-
? intval(GeneralUtility::_GP('pid'))
106-
: 0;
107-
}
108-
109-
/**
110-
* @return bool
111-
*/
112-
private function getFrontendControllerIsInitialized(): bool
113-
{
114-
return isset($GLOBALS['TSFE'])
115-
&& is_object($GLOBALS['TSFE'])
116-
&& !($GLOBALS['TSFE'] instanceof stdClass);
117-
}
118-
119-
/**
120-
* Configure the given frontend controller
121-
*
122-
* @param TypoScriptFrontendController $frontendController
123-
* @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
124-
* @throws \TYPO3\CMS\Core\Http\ImmediateResponseException
125-
*/
126-
private function configureFrontendController(TypoScriptFrontendController $frontendController)
127-
{
128-
$frontendController->initTemplate();
129-
130-
if (!is_array($frontendController->page)) {
131-
$frontendController->page = [];
132-
}
133-
134-
// Build an instance of ContentObjectRenderer
135-
$frontendController->newCObj();
136-
137-
// Add the FE user
138-
$frontendController->fe_user = EidUtility::initFeUser();
139-
140-
$frontendController->determineId();
141-
$frontendController->getConfigArray();
142-
143-
$this->detectAndSetRequestedLanguage($frontendController);
144-
//try {
145-
// $frontendController->settingLanguage();
146-
//} catch (\RuntimeException $exception) {
147-
//}
148-
$frontendController->settingLocale();
149-
}
27+
private $request;
15028

15129
/**
152-
* Configure the system to use the requested language UID
153-
*
154-
* @param TypoScriptFrontendController $frontendController
30+
* @var LanguageBootstrap
15531
*/
156-
private function detectAndSetRequestedLanguage(TypoScriptFrontendController $frontendController)
157-
{
158-
if (!isset($GLOBALS['TYPO3_REQUEST']) || !class_exists(SiteMatcher::class)) {
159-
$this->setRequestedLanguage($frontendController, $this->getRequestedLanguageUid($frontendController));
160-
161-
return;
162-
}
163-
164-
// support new TYPO3 v9.2 Site Handling until middleware concept is implemented
165-
// see https://github.com/cundd/rest/issues/59
166-
/** @var ServerRequestInterface $request */
167-
$request = $GLOBALS['TYPO3_REQUEST'];
168-
169-
/**
170-
* Try to detect the language ID from request parameters or headers. If the SiteMatcher detects a language this
171-
* fallback will **not** be used
172-
*
173-
* @var int|null $fallbackLanguageId
174-
*/
175-
$fallbackLanguageId = (int)($request->getQueryParams()['L']
176-
?? $request->getParsedBody()['L']
177-
?? $this->getRequestedLanguageUid($frontendController));
178-
179-
180-
/** @var SiteRouteResult $routeResult */
181-
$routeResult = GeneralUtility::makeInstance(SiteMatcher::class)->matchRequest($request);
182-
183-
184-
$site = $routeResult->getSite();
185-
$language = $routeResult->getLanguage();
186-
187-
// If TYPO3 could not determine the language for the request use the detected fallback
188-
if (!$language && $fallbackLanguageId !== null) {
189-
$language = $site->getLanguageById($fallbackLanguageId);
190-
}
191-
192-
$request = $request->withAttribute('site', $site);
193-
$request = $request->withAttribute('language', $language);
194-
$request = $request->withAttribute('routing', $routeResult);
195-
196-
// Patch the original Request so that at least `site` and `routing` are defined
197-
$GLOBALS['TYPO3_REQUEST'] = $request
198-
->withAttribute('site', $site)
199-
->withAttribute('language', $language)
200-
->withAttribute('routing', $routeResult);
201-
202-
// Set language if defined
203-
if ($language && $language->getLanguageId() !== null) {
204-
$this->setRequestedLanguage($frontendController, $language->getLanguageId());
205-
} else {
206-
$this->setRequestedLanguage($frontendController, $fallbackLanguageId);
207-
}
208-
}
32+
private $languageBootstrap;
20933

21034
/**
211-
* Detect the language UID for the requested language
212-
*
213-
* @param TypoScriptFrontendController $frontendController
214-
* @return int|null
35+
* Bootstrap constructor
21536
*/
216-
private function getRequestedLanguageUid(TypoScriptFrontendController $frontendController): ?int
37+
public function __construct()
21738
{
218-
if (GeneralUtility::_GP('L') !== null) {
219-
return (int)GeneralUtility::_GP('L');
220-
}
221-
if (GeneralUtility::_GP('locale') !== null) {
222-
$languageId = $this->getLanguageIdForCode($frontendController, GeneralUtility::_GP('locale'));
223-
if ($languageId !== null) {
224-
return $languageId;
225-
}
226-
}
227-
228-
// Test the full HTTP_ACCEPT_LANGUAGE header
229-
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
230-
$languageId = $this->getLanguageIdForCode(
231-
$frontendController,
232-
(string)$_SERVER['HTTP_ACCEPT_LANGUAGE']
233-
);
234-
235-
if ($languageId !== null) {
236-
return $languageId;
237-
}
238-
}
239-
240-
// Retrieve and test the parsed header
241-
$languageCode = $this->getRequestedLanguageCode();
242-
if ($languageCode !== null) {
243-
$languageId = $this->getLanguageIdForCode($frontendController, $languageCode);
244-
if ($languageId !== null) {
245-
return $languageId;
246-
}
247-
}
248-
249-
return null;
39+
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
40+
$this->coreBootstrap = $objectManager->get(Core::class);
41+
$this->languageBootstrap = $objectManager->get(LanguageBootstrap::class);
42+
$this->request = ServerRequestFactory::fromGlobals();
25043
}
25144

25245
/**
253-
* Retrieve the TypoScript configuration for the given key path
46+
* Initializes the TYPO3 environment
25447
*
255-
* @param string $keyPath
256-
* @param TypoScriptFrontendController $typoScriptFrontendController
257-
* @return mixed
48+
* @param TypoScriptFrontendController|null $frontendController
49+
* @return TypoScriptFrontendController
50+
* @throws ServiceUnavailableException
51+
* @deprecated will be removed in 5.0. Use \Cundd\Rest\Bootstrap\Core::initialize() instead
25852
*/
259-
private function readConfigurationFromTyposcript(
260-
string $keyPath,
261-
TypoScriptFrontendController $typoScriptFrontendController
53+
public function init(
54+
/** @noinspection PhpUnusedParameterInspection */ TypoScriptFrontendController $frontendController = null
26255
) {
263-
$keyPathParts = explode('.', (string)$keyPath);
264-
$currentValue = $typoScriptFrontendController->tmpl->setup;
265-
266-
foreach ($keyPathParts as $currentKey) {
267-
if (isset($currentValue[$currentKey . '.'])) {
268-
$currentValue = $currentValue[$currentKey . '.'];
269-
} elseif (isset($currentValue[$currentKey])) {
270-
$currentValue = $currentValue[$currentKey];
271-
} else {
272-
return null;
273-
}
274-
}
275-
276-
return $currentValue;
56+
return $this->coreBootstrap->initialize($this->request);
27757
}
27858

27959
/**
280-
* Detect the requested language
60+
* Initialize language object
28161
*
282-
* @return null|string
62+
* @deprecated will be removed in 5.0. Use \Cundd\Rest\Bootstrap\LanguageBootstrap::initializeLanguageObject() instead
28363
*/
284-
private function getRequestedLanguageCode(): ?string
285-
{
286-
if (class_exists('Locale') && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
287-
/** @noinspection PhpComposerExtensionStubsInspection */
288-
return Locale::getPrimaryLanguage(Locale::acceptFromHttp($_SERVER['HTTP_ACCEPT_LANGUAGE']));
289-
}
290-
291-
return null;
292-
}
293-
294-
/**
295-
* @param TypoScriptFrontendController $frontendController
296-
* @param string $languageCode
297-
* @return int
298-
*/
299-
private function getLanguageIdForCode(TypoScriptFrontendController $frontendController, string $languageCode): ?int
64+
public function initializeLanguageObject()
30065
{
301-
$value = $this->readConfigurationFromTyposcript(
302-
'plugin.tx_rest.settings.languages.' . $languageCode,
303-
$frontendController
304-
);
305-
if (is_int($value)) {
306-
return $value;
307-
} elseif (is_string($value)) {
308-
return trim($value) === '' ? null : (int)$value;
309-
} else {
310-
return null;
311-
}
312-
}
313-
314-
/**
315-
* @param TypoScriptFrontendController $frontendController
316-
* @param int|null $requestedLanguageUid
317-
*/
318-
private function setRequestedLanguage(
319-
TypoScriptFrontendController $frontendController,
320-
?int $requestedLanguageUid
321-
): void {
322-
if (null !== $requestedLanguageUid) {
323-
$frontendController->config['config']['sys_language_uid'] = $requestedLanguageUid;
324-
// Add LinkVars and language to work with correct localized labels
325-
$frontendController->config['config']['linkVars'] = 'L(int)';
326-
$frontendController->config['config']['language'] = $this->getRequestedLanguageCode();
327-
}
66+
$this->languageBootstrap->initializeLanguageObject($this->request);
32867
}
32968
}

0 commit comments

Comments
 (0)