2479fd150056e20d45aadac5cea738e1dc9468eb
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / SystemEnvironment / SetupCheck.php
1 <?php
2 namespace TYPO3\CMS\Install\SystemEnvironment;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Service\OpcodeCacheService;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Install\Status;
20
21 /**
22 * Check TYPO3 setup status
23 *
24 * This class is a hardcoded requirement check for the TYPO3 setup.
25 *
26 * The status messages and title *must not* include HTML, use plain
27 * text only. The return values of this class are not bound to HTML
28 * and can be used in different scopes (eg. as json array).
29 */
30 class SetupCheck
31 {
32 /**
33 * Get all status information as array with status objects
34 *
35 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
36 */
37 public function getStatus()
38 {
39 $status = [];
40
41 $status[] = $this->checkTrustedHostPattern();
42 $status[] = $this->checkDownloadsPossible();
43 $status[] = $this->checkSystemLocale();
44 $status[] = $this->checkLocaleWithUTF8filesystem();
45 $status[] = $this->checkSomePhpOpcodeCacheIsLoaded();
46 $status[] = $this->isTrueTypeFontWorking();
47 $status[] = $this->checkLibXmlBug();
48
49 return $status;
50 }
51
52 /**
53 * Checks the status of the trusted hosts pattern check
54 *
55 * @return Status\StatusInterface
56 */
57 protected function checkTrustedHostPattern()
58 {
59 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] === GeneralUtility::ENV_TRUSTED_HOSTS_PATTERN_ALLOW_ALL) {
60 $status = new Status\WarningStatus();
61 $status->setTitle('Trusted hosts pattern is insecure');
62 $status->setMessage('Trusted hosts pattern is configured to allow all header values. Check the pattern defined in Install Tool -> All configuration -> System -> trustedHostsPattern and adapt it to expected host value(s).');
63 } elseif (PHP_SAPI === 'cli') {
64 $status = new Status\NoticeStatus();
65 $status->setTitle('Trusted hosts pattern cannot be checked on the CLI');
66 } else {
67 if (GeneralUtility::hostHeaderValueMatchesTrustedHostsPattern($_SERVER['HTTP_HOST'])) {
68 $status = new Status\OkStatus();
69 $status->setTitle('Trusted hosts pattern is configured to allow current host value.');
70 } else {
71 $status = new Status\ErrorStatus();
72 $status->setTitle('Trusted hosts pattern mismatch');
73 $status->setMessage('The trusted hosts pattern will be configured to allow all header values. This is because your $SERVER_NAME is "' . htmlspecialchars($_SERVER['SERVER_NAME']) . '" while your HTTP_HOST is "' . htmlspecialchars($_SERVER['HTTP_HOST']) . '". Check the pattern defined in Install Tool -> All configuration -> System -> trustedHostsPattern and adapt it to expected host value(s).');
74 }
75 }
76
77 return $status;
78 }
79
80 /**
81 * Check if it is possible to download external data (e.g. TER)
82 * Either allow_url_fopen must be enabled or curl must be used
83 *
84 * @return Status\OkStatus|Status\WarningStatus
85 */
86 protected function checkDownloadsPossible()
87 {
88 $allowUrlFopen = (bool)ini_get('allow_url_fopen');
89 $curlEnabled = function_exists('curl_version');
90 if ($allowUrlFopen || $curlEnabled) {
91 $status = new Status\OkStatus();
92 $status->setTitle('Fetching external URLs is allowed');
93 } else {
94 $status = new Status\WarningStatus();
95 $status->setTitle('Fetching external URLs is not allowed');
96 $status->setMessage(
97 'Either enable PHP runtime setting "allow_url_fopen"' . LF . 'or compile curl into your PHP with --with-curl.'
98 );
99 }
100
101 return $status;
102 }
103
104 /**
105 * Check if systemLocale setting is correct (locale exists in the OS)
106 *
107 * @return Status\StatusInterface
108 */
109 protected function checkSystemLocale()
110 {
111 $currentLocale = setlocale(LC_CTYPE, 0);
112
113 // On Windows an empty locale value uses the regional settings from the Control Panel
114 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') {
115 $status = new Status\InfoStatus();
116 $status->setTitle('Empty systemLocale setting');
117 $status->setMessage(
118 '$GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is not set. This is fine as long as no UTF-8' .
119 ' file system is used.'
120 );
121 } elseif (setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']) === false) {
122 $status = new Status\ErrorStatus();
123 $status->setTitle('Incorrect systemLocale setting');
124 $status->setMessage(
125 'Current value of the $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] is incorrect. A locale with' .
126 ' this name doesn\'t exist in the operating system.'
127 );
128 setlocale(LC_CTYPE, $currentLocale);
129 } else {
130 $status = new Status\OkStatus();
131 $status->setTitle('System locale is correct');
132 }
133
134 return $status;
135 }
136
137 /**
138 * Checks whether we can use file names with UTF-8 characters.
139 * Configured system locale must support UTF-8 when UTF8filesystem is set
140 *
141 * @return Status\StatusInterface
142 */
143 protected function checkLocaleWithUTF8filesystem()
144 {
145 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']) {
146 // On Windows an empty local value uses the regional settings from the Control Panel
147 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale'] === '' && TYPO3_OS !== 'WIN') {
148 $status = new Status\ErrorStatus();
149 $status->setTitle('System locale not set on UTF-8 file system');
150 $status->setMessage(
151 '$GLOBALS[TYPO3_CONF_VARS][SYS][UTF8filesystem] is set, but $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale]' .
152 ' is empty. Make sure a valid locale which supports UTF-8 is set.'
153 );
154 } else {
155 $testString = 'ÖöĄĆŻĘĆćążąęó.jpg';
156 $currentLocale = setlocale(LC_CTYPE, 0);
157 $quote = TYPO3_OS === 'WIN' ? '"' : '\'';
158 setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
159 if (escapeshellarg($testString) === $quote . $testString . $quote) {
160 $status = new Status\OkStatus();
161 $status->setTitle('File names with UTF-8 characters can be used.');
162 } else {
163 $status = new Status\ErrorStatus();
164 $status->setTitle('System locale setting doesn\'t support UTF-8 file names.');
165 $status->setMessage(
166 'Please check your $GLOBALS[TYPO3_CONF_VARS][SYS][systemLocale] setting.'
167 );
168 }
169 setlocale(LC_CTYPE, $currentLocale);
170 }
171 } else {
172 $status = new Status\OkStatus();
173 $status->setTitle('Skipping test, as UTF8filesystem is not enabled.');
174 }
175
176 return $status;
177 }
178
179 /**
180 * Check if some opcode cache is loaded
181 *
182 * @return Status\StatusInterface
183 */
184 protected function checkSomePhpOpcodeCacheIsLoaded()
185 {
186 // Link to our wiki page, so we can update opcode cache issue information independent of TYPO3 CMS releases.
187 $wikiLink = 'For more information take a look in our wiki ' . TYPO3_URL_WIKI_OPCODECACHE . '.';
188 $opcodeCaches = GeneralUtility::makeInstance(OpcodeCacheService::class)->getAllActive();
189 if (empty($opcodeCaches)) {
190 // Set status to notice. It needs to be notice so email won't be triggered.
191 $status = new Status\NoticeStatus();
192 $status->setTitle('No PHP opcode cache loaded');
193 $status->setMessage(
194 'PHP opcode caches hold a compiled version of executed PHP scripts in' .
195 ' memory and do not require to recompile a script each time it is accessed.' .
196 ' This can be a massive performance improvement and can reduce the load on a' .
197 ' server in general. A parse time reduction by factor three for fully cached' .
198 ' pages can be achieved easily if using an opcode cache.' .
199 LF . $wikiLink
200 );
201 } else {
202 $status = new Status\OkStatus();
203 $message = '';
204 foreach ($opcodeCaches as $opcodeCache => $properties) {
205 $message .= 'Name: ' . $opcodeCache . ' Version: ' . $properties['version'];
206 $message .= LF;
207 if ($properties['error']) {
208 // Set status to error if not already set
209 if ($status->getSeverity() !== 'error') {
210 $status = new Status\ErrorStatus();
211 }
212 $message .= ' This opcode cache is marked as malfunctioning by the TYPO3 CMS Team.';
213 } elseif ($properties['canInvalidate']) {
214 $message .= ' This opcode cache should work correctly and has good performance.';
215 } else {
216 // Set status to notice if not already error set. It needs to be notice so email won't be triggered.
217 if ($status->getSeverity() !== 'error' || $status->getSeverity() !== 'warning') {
218 $status = new Status\NoticeStatus();
219 }
220 $message .= ' This opcode cache may work correctly but has medium performance.';
221 }
222 $message .= LF;
223 }
224 $message .= $wikiLink;
225 // Set title of status depending on serverity
226 switch ($status->getSeverity()) {
227 case 'error':
228 $status->setTitle('A possibly malfunctioning PHP opcode cache is loaded');
229 break;
230 case 'warning':
231 $status->setTitle('A PHP opcode cache is loaded which may cause problems');
232 break;
233 case 'ok':
234 default:
235 $status->setTitle('A PHP opcode cache is loaded');
236 break;
237 }
238 $status->setMessage($message);
239 }
240
241 return $status;
242 }
243
244 /**
245 * Create true type font test image
246 *
247 * @return Status\StatusInterface
248 */
249 protected function isTrueTypeFontWorking()
250 {
251 if (function_exists('imageftbbox')) {
252 // 20 Pixels at 96 DPI
253 $fontSize = (20 / 96 * 72);
254 $textDimensions = @imageftbbox(
255 $fontSize,
256 0,
257 __DIR__ . '/../../Resources/Private/Font/vera.ttf',
258 'Testing true type support'
259 );
260 $fontBoxWidth = $textDimensions[2] - $textDimensions[0];
261 if ($fontBoxWidth < 300 && $fontBoxWidth > 200) {
262 $status = new Status\OkStatus();
263 $status->setTitle('FreeType True Type Font DPI');
264 $status->setMessage('Fonts are rendered by FreeType library. ' .
265 'We need to ensure that the final dimensions are as expected. ' .
266 'This server renderes fonts based on 96 DPI correctly');
267 } else {
268 $status = new Status\NoticeStatus();
269 $status->setTitle('FreeType True Type Font DPI');
270 $status->setMessage('Fonts are rendered by FreeType library. ' .
271 'This server does not render fonts as expected. ' .
272 'Please check your FreeType 2 module.');
273 }
274 } else {
275 $status = new Status\ErrorStatus();
276 $status->setTitle('PHP GD library freetype2 support missing');
277 $status->setMessage(
278 'The core relies on GD library compiled into PHP with freetype2' .
279 ' support. This is missing on your system. Please install it.'
280 );
281 }
282
283 return $status;
284 }
285
286 /**
287 * Check for bug in libxml
288 *
289 * @return Status\StatusInterface
290 */
291 protected function checkLibXmlBug()
292 {
293 $sampleArray = ['Test>><<Data'];
294 $xmlContent = '<numIndex index="0">Test&gt;&gt;&lt;&lt;Data</numIndex>' . LF;
295 $xml = GeneralUtility::array2xml($sampleArray, '', -1);
296
297 if ($xmlContent !== $xml) {
298 $status = new Status\ErrorStatus();
299 $status->setTitle('PHP libxml bug present');
300 $status->setMessage(
301 'Some hosts have problems saving ">><<" in a flexform.' .
302 ' To fix this, enable [BE][flexformForceCDATA] in' .
303 ' All Configuration.'
304 );
305 } else {
306 $status = new Status\OkStatus();
307 $status->setTitle('PHP libxml bug not present');
308 }
309
310 return $status;
311 }
312 }