[!!!][TASK] Make TimeTracker a singleton
[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 = array();
28
29 /**
30 * @var array error stack
31 */
32 public $error = array();
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 = array();
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 = array();
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 * @return void
152 */
153 public function devLog($msg, $severity = 0, $dataVar = false)
154 {
155 if ($this->writeDevLog) {
156 GeneralUtility::devLog($msg, $this->info['serviceKey'], $severity, $dataVar);
157 }
158 }
159
160 /**
161 * Puts an error on the error stack. Calling without parameter adds a general error.
162 *
163 * @param int $errNum Error number (see T3_ERR_SV_* constants)
164 * @param string $errMsg Error message
165 * @return void
166 */
167 public function errorPush($errNum = T3_ERR_SV_GENERAL, $errMsg = 'Unspecified error occurred')
168 {
169 array_push($this->error, array('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 * @return void
179 */
180 public function errorPull()
181 {
182 array_pop($this->error);
183 }
184
185 /**
186 * Returns the last error number from the error stack.
187 *
188 * @return int|bool Error number (or TRUE if no error)
189 */
190 public function getLastError()
191 {
192 // Means all is ok - no error
193 $lastError = true;
194 if (!empty($this->error)) {
195 $error = end($this->error);
196 $lastError = $error['nr'];
197 }
198 return $lastError;
199 }
200
201 /**
202 * Returns the last message from the error stack.
203 *
204 * @return string Error message
205 */
206 public function getLastErrorMsg()
207 {
208 $lastErrorMessage = '';
209 if (!empty($this->error)) {
210 $error = end($this->error);
211 $lastErrorMessage = $error['msg'];
212 }
213 return $lastErrorMessage;
214 }
215
216 /**
217 * Returns all error messages as array.
218 *
219 * @return array Error messages
220 */
221 public function getErrorMsgArray()
222 {
223 $errArr = array();
224 if (!empty($this->error)) {
225 foreach ($this->error as $error) {
226 $errArr[] = $error['msg'];
227 }
228 }
229 return $errArr;
230 }
231
232 /**
233 * Returns the last array from the error stack.
234 *
235 * @return array Error number and message
236 */
237 public function getLastErrorArray()
238 {
239 return end($this->error);
240 }
241
242 /**
243 * Reset the error stack.
244 *
245 * @return void
246 */
247 public function resetErrors()
248 {
249 $this->error = array();
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 (!\TYPO3\CMS\Core\Utility\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 * @return void
281 */
282 public function deactivateService()
283 {
284 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::deactivateService($this->info['serviceType'], $this->info['serviceKey']);
285 }
286
287 /***************************************
288 *
289 * IO tools
290 *
291 ***************************************/
292 /**
293 * Check if a file exists and is readable.
294 *
295 * @param string $absFile File name with absolute path.
296 * @return string|bool File name or FALSE.
297 */
298 public function checkInputFile($absFile)
299 {
300 $checkResult = false;
301 if (GeneralUtility::isAllowedAbsPath($absFile) && @is_file($absFile)) {
302 if (@is_readable($absFile)) {
303 $checkResult = $absFile;
304 } else {
305 $this->errorPush(T3_ERR_SV_FILE_READ, 'File is not readable: ' . $absFile);
306 }
307 } else {
308 $this->errorPush(T3_ERR_SV_FILE_NOT_FOUND, 'File not found: ' . $absFile);
309 }
310 return $checkResult;
311 }
312
313 /**
314 * Read content from a file a file.
315 *
316 * @param string $absFile File name to read from.
317 * @param int $length Maximum length to read. If empty the whole file will be read.
318 * @return string|bool $content or FALSE
319 */
320 public function readFile($absFile, $length = 0)
321 {
322 $out = false;
323 if ($this->checkInputFile($absFile)) {
324 $out = file_get_contents($absFile);
325 if ($out === false) {
326 $this->errorPush(T3_ERR_SV_FILE_READ, 'Can not read from file: ' . $absFile);
327 }
328 }
329 return $out;
330 }
331
332 /**
333 * Write content to a file.
334 *
335 * @param string $content Content to write to the file
336 * @param string $absFile File name to write into. If empty a temp file will be created.
337 * @return string|bool File name or FALSE
338 */
339 public function writeFile($content, $absFile = '')
340 {
341 if (!$absFile) {
342 $absFile = $this->tempFile($this->prefixId);
343 }
344 if ($absFile && GeneralUtility::isAllowedAbsPath($absFile)) {
345 if ($fd = @fopen($absFile, 'wb')) {
346 @fwrite($fd, $content);
347 @fclose($fd);
348 } else {
349 $this->errorPush(T3_ERR_SV_FILE_WRITE, 'Can not write to file: ' . $absFile);
350 $absFile = false;
351 }
352 }
353 return $absFile;
354 }
355
356 /**
357 * Create a temporary file.
358 *
359 * @param string $filePrefix File prefix.
360 * @return string|bool File name or FALSE
361 */
362 public function tempFile($filePrefix)
363 {
364 $absFile = GeneralUtility::tempnam($filePrefix);
365 if ($absFile) {
366 $ret = $absFile;
367 $this->registerTempFile($absFile);
368 } else {
369 $ret = false;
370 $this->errorPush(T3_ERR_SV_FILE_WRITE, 'Can not create temp file.');
371 }
372 return $ret;
373 }
374
375 /**
376 * Register file which should be deleted afterwards.
377 *
378 * @param string File name with absolute path.
379 * @return void
380 */
381 public function registerTempFile($absFile)
382 {
383 if (!isset($this->shutdownRegistry[__METHOD__])) {
384 register_shutdown_function(array($this, 'unlinkTempFiles'));
385 $this->shutdownRegistry[__METHOD__] = true;
386 }
387 $this->tempFiles[] = $absFile;
388 }
389
390 /**
391 * Delete registered temporary files.
392 *
393 * @return void
394 */
395 public function unlinkTempFiles()
396 {
397 foreach ($this->tempFiles as $absFile) {
398 GeneralUtility::unlink_tempfile($absFile);
399 }
400 $this->tempFiles = array();
401 }
402
403 /***************************************
404 *
405 * IO input
406 *
407 ***************************************/
408 /**
409 * Set the input content for service processing.
410 *
411 * @param mixed $content Input content (going into ->inputContent)
412 * @param string $type The type of the input content (or file). Might be the same as the service subtypes.
413 * @return void
414 */
415 public function setInput($content, $type = '')
416 {
417 $this->inputContent = $content;
418 $this->inputFile = '';
419 $this->inputType = $type;
420 }
421
422 /**
423 * Set the input file name for service processing.
424 *
425 * @param string $absFile File name
426 * @param string $type The type of the input content (or file). Might be the same as the service subtypes.
427 * @return void
428 */
429 public function setInputFile($absFile, $type = '')
430 {
431 $this->inputContent = '';
432 $this->inputFile = $absFile;
433 $this->inputType = $type;
434 }
435
436 /**
437 * Get the input content.
438 * Will be read from input file if needed. (That is if ->inputContent is empty and ->inputFile is not)
439 *
440 * @return mixed
441 */
442 public function getInput()
443 {
444 if ($this->inputContent == '') {
445 $this->inputContent = $this->readFile($this->inputFile);
446 }
447 return $this->inputContent;
448 }
449
450 /**
451 * Get the input file name.
452 * If the content was set by setContent a file will be created.
453 *
454 * @param string $createFile File name. If empty a temp file will be created.
455 * @return string File name or FALSE if no input or file error.
456 */
457 public function getInputFile($createFile = '')
458 {
459 if ($this->inputFile) {
460 $this->inputFile = $this->checkInputFile($this->inputFile);
461 } elseif ($this->inputContent) {
462 $this->inputFile = $this->writeFile($this->inputContent, $createFile);
463 }
464 return $this->inputFile;
465 }
466
467 /***************************************
468 *
469 * IO output
470 *
471 ***************************************/
472 /**
473 * Set the output file name.
474 *
475 * @param string $absFile File name
476 * @return void
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 * @return void
540 */
541 public function reset()
542 {
543 $this->unlinkTempFiles();
544 $this->resetErrors();
545 $this->out = '';
546 $this->inputFile = '';
547 $this->inputContent = '';
548 $this->inputType = '';
549 $this->outputFile = '';
550 }
551
552 /**
553 * Clean up the service.
554 * Child classes should explicitly call parent::__destruct() in their destructors for this to work
555 *
556 * @return void
557 */
558 public function __destruct()
559 {
560 $this->unlinkTempFiles();
561 }
562 }