[TASK] Make sysext/extbase/Tests/Unit notice free
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Site / Entity / Site.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Core\Site\Entity;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Core\Error\PageErrorHandler\FluidPageErrorHandler;
20 use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
21 use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
22 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
23 use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26 /**
27 * Entity representing a single site with available languages
28 */
29 class Site implements SiteInterface
30 {
31 protected const ERRORHANDLER_TYPE_PAGE = 'Page';
32 protected const ERRORHANDLER_TYPE_FLUID = 'Fluid';
33 protected const ERRORHANDLER_TYPE_PHP = 'PHP';
34
35 /**
36 * @var string
37 */
38 protected $identifier;
39
40 /**
41 * @var string
42 */
43 protected $base;
44
45 /**
46 * @var int
47 */
48 protected $rootPageId;
49
50 /**
51 * Any attributes for this site
52 * @var array
53 */
54 protected $configuration;
55
56 /**
57 * @var SiteLanguage[]
58 */
59 protected $languages;
60
61 /**
62 * @var array
63 */
64 protected $errorHandlers;
65
66 /**
67 * Sets up a site object, and its languages and error handlers
68 *
69 * @param string $identifier
70 * @param int $rootPageId
71 * @param array $configuration
72 */
73 public function __construct(string $identifier, int $rootPageId, array $configuration)
74 {
75 $this->identifier = $identifier;
76 $this->rootPageId = $rootPageId;
77 $this->configuration = $configuration;
78 $configuration['languages'] = $configuration['languages'] ?: [
79 0 => [
80 'languageId' => 0,
81 'title' => 'Default',
82 'navigationTitle' => '',
83 'typo3Language' => 'default',
84 'flag' => 'us',
85 'locale' => 'en_US.UTF-8',
86 'iso-639-1' => 'en',
87 'hreflang' => 'en-US',
88 'direction' => '',
89 ]
90 ];
91 $this->base = $this->sanitizeBaseUrl($configuration['base'] ?? '');
92 foreach ($configuration['languages'] as $languageConfiguration) {
93 $languageUid = (int)$languageConfiguration['languageId'];
94 // site language has defined its own base, this is the case most of the time.
95 if (!empty($languageConfiguration['base'])) {
96 $base = $languageConfiguration['base'];
97 $base = $this->sanitizeBaseUrl($base);
98 $baseParts = parse_url($base);
99 // no host given by the language-specific base, so lets prefix the main site base
100 if (empty($baseParts['scheme']) && empty($baseParts['host'])) {
101 $base = rtrim($this->base, '/') . '/' . ltrim($base, '/');
102 $base = $this->sanitizeBaseUrl($base);
103 }
104 } else {
105 // Language configuration does not have a base defined
106 // So the main site base is used (usually done for default languages)
107 $base = $this->sanitizeBaseUrl(rtrim($this->base, '/') . '/');
108 }
109 $this->languages[$languageUid] = new SiteLanguage(
110 $this,
111 $languageUid,
112 $languageConfiguration['locale'],
113 $base,
114 $languageConfiguration
115 );
116 }
117 foreach ($configuration['errorHandling'] ?? [] as $errorHandlingConfiguration) {
118 $code = $errorHandlingConfiguration['errorCode'];
119 unset($errorHandlingConfiguration['errorCode']);
120 $this->errorHandlers[(int)$code] = $errorHandlingConfiguration;
121 }
122 }
123
124 /**
125 * Gets the identifier of this site,
126 * mainly used when maintaining / configuring sites.
127 *
128 * @return string
129 */
130 public function getIdentifier(): string
131 {
132 return $this->identifier;
133 }
134
135 /**
136 * Returns the base URL of this site
137 *
138 * @return string
139 */
140 public function getBase(): string
141 {
142 return $this->base;
143 }
144
145 /**
146 * Returns the root page ID of this site
147 *
148 * @return int
149 */
150 public function getRootPageId(): int
151 {
152 return $this->rootPageId;
153 }
154
155 /**
156 * Returns all available languages of this site
157 *
158 * @return SiteLanguage[]
159 */
160 public function getLanguages(): array
161 {
162 return $this->languages;
163 }
164
165 /**
166 * Returns a language of this site, given by the sys_language_uid
167 *
168 * @param int $languageId
169 * @return SiteLanguage
170 * @throws \InvalidArgumentException
171 */
172 public function getLanguageById(int $languageId): SiteLanguage
173 {
174 if (isset($this->languages[$languageId])) {
175 return $this->languages[$languageId];
176 }
177 throw new \InvalidArgumentException(
178 'Language ' . $languageId . ' does not exist on site ' . $this->identifier . '.',
179 1522960188
180 );
181 }
182
183 /**
184 * Returns a ready-to-use error handler, to be used within the ErrorController
185 *
186 * @param int $statusCode
187 * @return PageErrorHandlerInterface
188 * @throws PageErrorHandlerNotConfiguredException
189 * @throws InvalidPageErrorHandlerException
190 */
191 public function getErrorHandler(int $statusCode): PageErrorHandlerInterface
192 {
193 $errorHandlerConfiguration = $this->errorHandlers[$statusCode] ?? null;
194 switch ($errorHandlerConfiguration['errorHandler']) {
195 case self::ERRORHANDLER_TYPE_FLUID:
196 return GeneralUtility::makeInstance(FluidPageErrorHandler::class, $statusCode, $errorHandlerConfiguration);
197 case self::ERRORHANDLER_TYPE_PAGE:
198 return GeneralUtility::makeInstance(PageContentErrorHandler::class, $statusCode, $errorHandlerConfiguration);
199 case self::ERRORHANDLER_TYPE_PHP:
200 $handler = GeneralUtility::makeInstance($errorHandlerConfiguration['errorPhpClassFQCN'], $statusCode, $errorHandlerConfiguration);
201 // Check if the interface is implemented
202 if (!($handler instanceof PageErrorHandlerInterface)) {
203 throw new InvalidPageErrorHandlerException('The configured error handler "' . (string)$errorHandlerConfiguration['errorPhpClassFQCN'] . '" for status code ' . $statusCode . ' must implement the PageErrorHandlerInterface.', 1527432330);
204 }
205 return $handler;
206 }
207 throw new PageErrorHandlerNotConfiguredException('No error handler given for the status code "' . $statusCode . '".', 1522495914);
208 }
209
210 /**
211 * Returns the whole configuration for this site
212 *
213 * @return array
214 */
215 public function getConfiguration(): array
216 {
217 return $this->configuration;
218 }
219
220 /**
221 * Returns a single configuration attribute
222 *
223 * @param string $attributeName
224 * @return mixed
225 * @throws \InvalidArgumentException
226 */
227 public function getAttribute(string $attributeName)
228 {
229 if (isset($this->configuration[$attributeName])) {
230 return $this->configuration[$attributeName];
231 }
232 throw new \InvalidArgumentException(
233 'Attribute ' . $attributeName . ' does not exist on site ' . $this->identifier . '.',
234 1522495954
235 );
236 }
237
238 /**
239 * If a site base contains "/" or "www.domain.com", it is ensured that
240 * parse_url() can handle this kind of configuration properly.
241 *
242 * @param string $base
243 * @return string
244 */
245 protected function sanitizeBaseUrl(string $base): string
246 {
247 // no protocol ("//") and the first part is no "/" (path), means that this is a domain like
248 // "www.domain.com/blabla", and we want to ensure that this one then gets a "no-scheme agnostic" part
249 if (!empty($base) && strpos($base, '//') === false && $base{0} !== '/') {
250 // either a scheme is added, or no scheme but with domain, or a path which is not absolute
251 // make the base prefixed with a slash, so it is recognized as path, not as domain
252 // treat as path
253 if (strpos($base, '.') === false) {
254 $base = '/' . $base;
255 } else {
256 // treat as domain name
257 $base = '//' . $base;
258 }
259 }
260 return $base;
261 }
262 }