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