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