[!!!][TASK] Use native trigger_error and ErrorHandler for deprecations
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Service / AbstractService.php
1 <?php
2 namespace TYPO3\CMS\Core\Service;
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 Psr\Log\LoggerAwareInterface;
18 use Psr\Log\LoggerAwareTrait;
19 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
20 use TYPO3\CMS\Core\Utility\CommandUtility;
21 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * Parent class for "Services" classes
26 */
27 abstract class AbstractService implements LoggerAwareInterface
28 {
29 use LoggerAwareTrait;
30
31 /**
32 * @var array service description array
33 */
34 public $info = [];
35
36 /**
37 * @var array error stack
38 */
39 public $error = [];
40
41 /**
42 * @var string The output content. That's what the services produced as result.
43 */
44 public $out = '';
45
46 /**
47 * @var string The file that should be processed.
48 */
49 public $inputFile = '';
50
51 /**
52 * @var string The content that should be processed.
53 */
54 public $inputContent = '';
55
56 /**
57 * @var string The type of the input content (or file). Might be the same as the service subtypes.
58 */
59 public $inputType = '';
60
61 /**
62 * @var string The file where the output should be written to.
63 */
64 public $outputFile = '';
65
66 /**
67 * Temporary files which have to be deleted
68 *
69 * @private
70 * @var array
71 */
72 public $tempFiles = [];
73
74 /**
75 * @var array list of registered shutdown functions; should be used to prevent registering the same function multiple times
76 */
77 protected $shutdownRegistry = [];
78
79 /**
80 * @var string Prefix for temporary files
81 */
82 protected $prefixId = '';
83
84 /***************************************
85 *
86 * Get service meta information
87 *
88 ***************************************/
89 /**
90 * Returns internal information array for service
91 *
92 * @return array Service description array
93 */
94 public function getServiceInfo()
95 {
96 return $this->info;
97 }
98
99 /**
100 * Returns the service key of the service
101 *
102 * @return string Service key
103 */
104 public function getServiceKey()
105 {
106 return $this->info['serviceKey'];
107 }
108
109 /**
110 * Returns the title of the service
111 *
112 * @return string Service title
113 */
114 public function getServiceTitle()
115 {
116 return $this->info['title'];
117 }
118
119 /**
120 * Returns service configuration values from the $TYPO3_CONF_VARS['SVCONF'] array
121 *
122 * @param string $optionName Name of the config option
123 * @param mixed $defaultValue Default configuration if no special config is available
124 * @param bool $includeDefaultConfig If set the 'default' config will be returned if no special config for this service is available (default: TRUE)
125 * @return mixed Configuration value for the service
126 */
127 public function getServiceOption($optionName, $defaultValue = '', $includeDefaultConfig = true)
128 {
129 $config = null;
130 $svOptions = $GLOBALS['TYPO3_CONF_VARS']['SVCONF'][$this->info['serviceType']];
131 if (isset($svOptions[$this->info['serviceKey']][$optionName])) {
132 $config = $svOptions[$this->info['serviceKey']][$optionName];
133 } elseif ($includeDefaultConfig && isset($svOptions['default'][$optionName])) {
134 $config = $svOptions['default'][$optionName];
135 }
136 if (!isset($config)) {
137 $config = $defaultValue;
138 }
139 return $config;
140 }
141
142 /***************************************
143 *
144 * Error handling
145 *
146 ***************************************/
147 /**
148 * Logs debug messages to the Logging API
149 *
150 * @param string $msg Debug message
151 * @param int $severity Severity: 0 is info, 1 is notice, 2 is warning, 3 is fatal error, -1 is "OK" message
152 * @param array|bool $dataVar additional data you want to pass to the logger.
153 * @deprecated
154 */
155 public function devLog($msg, $severity = 0, $dataVar = false)
156 {
157 trigger_error('Method devLog() is deprecated since v9 and will be removed with v10', E_USER_DEPRECATED);
158 $this->logger->debug($this->info['serviceKey'] . ': ' . $msg, (array)$dataVar);
159 }
160
161 /**
162 * Puts an error on the error stack. Calling without parameter adds a general error.
163 *
164 * @param int $errNum Error number (see T3_ERR_SV_* constants)
165 * @param string $errMsg Error message
166 */
167 public function errorPush($errNum = T3_ERR_SV_GENERAL, $errMsg = 'Unspecified error occurred')
168 {
169 $this->error[] = ['nr' => $errNum, 'msg' => $errMsg];
170 /** @var \TYPO3\CMS\Core\TimeTracker\TimeTracker $timeTracker */
171 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
172 $timeTracker->setTSlogMessage($errMsg, 2);
173 }
174
175 /**
176 * Removes the last error from the error stack.
177 */
178 public function errorPull()
179 {
180 array_pop($this->error);
181 }
182
183 /**
184 * Returns the last error number from the error stack.
185 *
186 * @return int|bool Error number (or TRUE if no error)
187 */
188 public function getLastError()
189 {
190 // Means all is ok - no error
191 $lastError = true;
192 if (!empty($this->error)) {
193 $error = end($this->error);
194 $lastError = $error['nr'];
195 }
196 return $lastError;
197 }
198
199 /**
200 * Returns the last message from the error stack.
201 *
202 * @return string Error message
203 */
204 public function getLastErrorMsg()
205 {
206 $lastErrorMessage = '';
207 if (!empty($this->error)) {
208 $error = end($this->error);
209 $lastErrorMessage = $error['msg'];
210 }
211 return $lastErrorMessage;
212 }
213
214 /**
215 * Returns all error messages as array.
216 *
217 * @return array Error messages
218 */
219 public function getErrorMsgArray()
220 {
221 $errArr = [];
222 if (!empty($this->error)) {
223 foreach ($this->error as $error) {
224 $errArr[] = $error['msg'];
225 }
226 }
227 return $errArr;
228 }
229
230 /**
231 * Returns the last array from the error stack.
232 *
233 * @return array Error number and message
234 */
235 public function getLastErrorArray()
236 {
237 return end($this->error);
238 }
239
240 /**
241 * Reset the error stack.
242 */
243 public function resetErrors()
244 {
245 $this->error = [];
246 }
247
248 /***************************************
249 *
250 * General service functions
251 *
252 ***************************************/
253 /**
254 * check the availability of external programs
255 *
256 * @param string $progList Comma list of programs 'perl,python,pdftotext'
257 * @return bool Return FALSE if one program was not found
258 */
259 public function checkExec($progList)
260 {
261 $ret = true;
262 $progList = GeneralUtility::trimExplode(',', $progList, true);
263 foreach ($progList as $prog) {
264 if (!CommandUtility::checkCommand($prog)) {
265 // Program not found
266 $this->errorPush(T3_ERR_SV_PROG_NOT_FOUND, 'External program not found: ' . $prog);
267 $ret = false;
268 }
269 }
270 return $ret;
271 }
272
273 /**
274 * Deactivate the service. Use this if the service fails at runtime and will not be available.
275 */
276 public function deactivateService()
277 {
278 ExtensionManagementUtility::deactivateService($this->info['serviceType'], $this->info['serviceKey']);
279 }
280
281 /***************************************
282 *
283 * IO tools
284 *
285 ***************************************/
286 /**
287 * Check if a file exists and is readable.
288 *
289 * @param string $absFile File name with absolute path.
290 * @return string|bool File name or FALSE.
291 */
292 public function checkInputFile($absFile)
293 {
294 $checkResult = false;
295 if (GeneralUtility::isAllowedAbsPath($absFile) && @is_file($absFile)) {
296 if (@is_readable($absFile)) {
297 $checkResult = $absFile;
298 } else {
299 $this->errorPush(T3_ERR_SV_FILE_READ, 'File is not readable: ' . $absFile);
300 }
301 } else {
302 $this->errorPush(T3_ERR_SV_FILE_NOT_FOUND, 'File not found: ' . $absFile);
303 }
304 return $checkResult;
305 }
306
307 /**
308 * Read content from a file a file.
309 *
310 * @param string $absFile File name to read from.
311 * @param int $length Maximum length to read. If empty the whole file will be read.
312 * @return string|bool $content or FALSE
313 */
314 public function readFile($absFile, $length = 0)
315 {
316 $out = false;
317 if ($this->checkInputFile($absFile)) {
318 $out = file_get_contents($absFile);
319 if ($out === false) {
320 $this->errorPush(T3_ERR_SV_FILE_READ, 'Can not read from file: ' . $absFile);
321 }
322 }
323 return $out;
324 }
325
326 /**
327 * Write content to a file.
328 *
329 * @param string $content Content to write to the file
330 * @param string $absFile File name to write into. If empty a temp file will be created.
331 * @return string|bool File name or FALSE
332 */
333 public function writeFile($content, $absFile = '')
334 {
335 if (!$absFile) {
336 $absFile = $this->tempFile($this->prefixId);
337 }
338 if ($absFile && GeneralUtility::isAllowedAbsPath($absFile)) {
339 if ($fd = @fopen($absFile, 'wb')) {
340 @fwrite($fd, $content);
341 @fclose($fd);
342 } else {
343 $this->errorPush(T3_ERR_SV_FILE_WRITE, 'Can not write to file: ' . $absFile);
344 $absFile = false;
345 }
346 }
347 return $absFile;
348 }
349
350 /**
351 * Create a temporary file.
352 *
353 * @param string $filePrefix File prefix.
354 * @return string|bool File name or FALSE
355 */
356 public function tempFile($filePrefix)
357 {
358 $absFile = GeneralUtility::tempnam($filePrefix);
359 if ($absFile) {
360 $ret = $absFile;
361 $this->registerTempFile($absFile);
362 } else {
363 $ret = false;
364 $this->errorPush(T3_ERR_SV_FILE_WRITE, 'Can not create temp file.');
365 }
366 return $ret;
367 }
368
369 /**
370 * Register file which should be deleted afterwards.
371 *
372 * @param string $absFile File name with absolute path.
373 */
374 public function registerTempFile($absFile)
375 {
376 if (!isset($this->shutdownRegistry[__METHOD__])) {
377 register_shutdown_function([$this, 'unlinkTempFiles']);
378 $this->shutdownRegistry[__METHOD__] = true;
379 }
380 $this->tempFiles[] = $absFile;
381 }
382
383 /**
384 * Delete registered temporary files.
385 */
386 public function unlinkTempFiles()
387 {
388 foreach ($this->tempFiles as $absFile) {
389 GeneralUtility::unlink_tempfile($absFile);
390 }
391 $this->tempFiles = [];
392 }
393
394 /***************************************
395 *
396 * IO input
397 *
398 ***************************************/
399 /**
400 * Set the input content for service processing.
401 *
402 * @param mixed $content Input content (going into ->inputContent)
403 * @param string $type The type of the input content (or file). Might be the same as the service subtypes.
404 */
405 public function setInput($content, $type = '')
406 {
407 $this->inputContent = $content;
408 $this->inputFile = '';
409 $this->inputType = $type;
410 }
411
412 /**
413 * Set the input file name for service processing.
414 *
415 * @param string $absFile File name
416 * @param string $type The type of the input content (or file). Might be the same as the service subtypes.
417 */
418 public function setInputFile($absFile, $type = '')
419 {
420 $this->inputContent = '';
421 $this->inputFile = $absFile;
422 $this->inputType = $type;
423 }
424
425 /**
426 * Get the input content.
427 * Will be read from input file if needed. (That is if ->inputContent is empty and ->inputFile is not)
428 *
429 * @return mixed
430 */
431 public function getInput()
432 {
433 if ($this->inputContent == '') {
434 $this->inputContent = $this->readFile($this->inputFile);
435 }
436 return $this->inputContent;
437 }
438
439 /**
440 * Get the input file name.
441 * If the content was set by setContent a file will be created.
442 *
443 * @param string $createFile File name. If empty a temp file will be created.
444 * @return string File name or FALSE if no input or file error.
445 */
446 public function getInputFile($createFile = '')
447 {
448 if ($this->inputFile) {
449 $this->inputFile = $this->checkInputFile($this->inputFile);
450 } elseif ($this->inputContent) {
451 $this->inputFile = $this->writeFile($this->inputContent, $createFile);
452 }
453 return $this->inputFile;
454 }
455
456 /***************************************
457 *
458 * IO output
459 *
460 ***************************************/
461 /**
462 * Set the output file name.
463 *
464 * @param string $absFile File name
465 */
466 public function setOutputFile($absFile)
467 {
468 $this->outputFile = $absFile;
469 }
470
471 /**
472 * Get the output content.
473 *
474 * @return mixed
475 */
476 public function getOutput()
477 {
478 if ($this->outputFile) {
479 $this->out = $this->readFile($this->outputFile);
480 }
481 return $this->out;
482 }
483
484 /**
485 * Get the output file name. If no output file is set, the ->out buffer is written to the file given by input parameter filename
486 *
487 * @param string $absFile Absolute filename to write to
488 * @return mixed
489 */
490 public function getOutputFile($absFile = '')
491 {
492 if (!$this->outputFile) {
493 $this->outputFile = $this->writeFile($this->out, $absFile);
494 }
495 return $this->outputFile;
496 }
497
498 /***************************************
499 *
500 * Service implementation
501 *
502 ***************************************/
503 /**
504 * Initialization of the service.
505 *
506 * The class have to do a strict check if the service is available.
507 * example: check if the perl interpreter is available which is needed to run an extern perl script.
508 *
509 * @return bool TRUE if the service is available
510 */
511 public function init()
512 {
513 // look in makeInstanceService()
514 $this->reset();
515 // Check for external programs which are defined by $info['exec']
516 if (trim($this->info['exec'])) {
517 if (!$this->checkExec($this->info['exec'])) {
518 }
519 }
520 return $this->getLastError() === true;
521 }
522
523 /**
524 * Resets the service.
525 * Will be called by init(). Should be used before every use if a service instance is used multiple times.
526 */
527 public function reset()
528 {
529 $this->unlinkTempFiles();
530 $this->resetErrors();
531 $this->out = '';
532 $this->inputFile = '';
533 $this->inputContent = '';
534 $this->inputType = '';
535 $this->outputFile = '';
536 }
537
538 /**
539 * Clean up the service.
540 * Child classes should explicitly call parent::__destruct() in their destructors for this to work
541 */
542 public function __destruct()
543 {
544 $this->unlinkTempFiles();
545 }
546 }