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