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