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