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