Fixed bug #17066: Wrong PHPDoc comment for t3lib_div::writeFileToTypo3tempDir
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_div.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2010 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains the reknown class "t3lib_div" with general purpose functions
29 *
30 * $Id$
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
32 * XHTML compliant
33 * Usage counts are based on search 22/2 2003 through whole source including tslib/
34 *
35 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
36 */
37 /**
38 * [CLASS/FUNCTION INDEX of SCRIPT]
39 *
40 *
41 *
42 * 232: class t3lib_div
43 *
44 * SECTION: GET/POST Variables
45 * 262: function _GP($var)
46 * 280: function _GET($var=NULL)
47 * 297: function _POST($var=NULL)
48 * 313: function _GETset($inputGet,$key='')
49 * 336: function GPvar($var,$strip=0)
50 * 353: function GParrayMerged($var)
51 *
52 * SECTION: IMAGE FUNCTIONS
53 * 397: function gif_compress($theFile, $type)
54 * 425: function png_to_gif_by_imagemagick($theFile)
55 * 450: function read_png_gif($theFile,$output_png=0)
56 *
57 * SECTION: STRING FUNCTIONS
58 * 499: function fixed_lgd($string,$origChars,$preStr='...')
59 * 524: function fixed_lgd_pre($string,$chars)
60 * 538: function fixed_lgd_cs($string,$chars)
61 * 555: function breakTextForEmail($str,$implChar=LF,$charWidth=76)
62 * 574: function breakLinesForEmail($str,$implChar=LF,$charWidth=76)
63 * 610: function cmpIP($baseIP, $list)
64 * 626: function cmpIPv4($baseIP, $list)
65 * 668: function cmpIPv6($baseIP, $list)
66 * 711: function IPv6Hex2Bin ($hex)
67 * 726: function normalizeIPv6($address)
68 * 782: function validIPv6($ip)
69 * 805: function cmpFQDN($baseIP, $list)
70 * 835: function inList($list,$item)
71 * 847: function rmFromList($element,$list)
72 * 863: function expandList($list)
73 * 894: function intInRange($theInt,$min,$max=2000000000,$zeroValue=0)
74 * 910: function intval_positive($theInt)
75 * 923: function int_from_ver($verNumberStr)
76 * 934: function compat_version($verNumberStr)
77 * 952: function md5int($str)
78 * 965: function shortMD5($input, $len=10)
79 * 978: function uniqueList($in_list, $secondParameter=NULL)
80 * 992: function split_fileref($fileref)
81 * 1030: function dirname($path)
82 * 1046: function modifyHTMLColor($color,$R,$G,$B)
83 * 1066: function modifyHTMLColorAll($color,$all)
84 * 1077: function rm_endcomma($string)
85 * 1090: function danish_strtoupper($string)
86 * 1105: function convUmlauts($str)
87 * 1118: function testInt($var)
88 * 1130: function isFirstPartOfStr($str,$partStr)
89 * 1146: function formatSize($sizeInBytes,$labels='')
90 * 1181: function convertMicrotime($microtime)
91 * 1195: function splitCalc($string,$operators)
92 * 1217: function calcPriority($string)
93 * 1258: function calcParenthesis($string)
94 * 1284: function htmlspecialchars_decode($value)
95 * 1299: function deHSCentities($str)
96 * 1312: function slashJS($string,$extended=0,$char="'")
97 * 1325: function rawUrlEncodeJS($str)
98 * 1337: function rawUrlEncodeFP($str)
99 * 1348: function validEmail($email)
100 * 1363: function formatForTextarea($content)
101 *
102 * SECTION: ARRAY FUNCTIONS
103 * 1394: function inArray($in_array,$item)
104 * 1411: function intExplode($delim, $string)
105 * 1430: function revExplode($delim, $string, $count=0)
106 * 1450: function trimExplode($delim, $string, $onlyNonEmptyValues=0)
107 * 1472: function uniqueArray($valueArray)
108 * 1484: function removeArrayEntryByValue($array,$cmpValue)
109 * 1513: function implodeArrayForUrl($name,$theArray,$str='',$skipBlank=0,$rawurlencodeParamName=0)
110 * 1538: function explodeUrl2Array($string,$multidim=FALSE)
111 * 1564: function compileSelectedGetVarsFromArray($varList,$getArray,$GPvarAlt=1)
112 * 1587: function addSlashesOnArray(&$theArray)
113 * 1611: function stripSlashesOnArray(&$theArray)
114 * 1633: function slashArray($arr,$cmd)
115 * 1650: function array_merge_recursive_overrule($arr0,$arr1,$notAddKeys=0,$includeEmtpyValues=true)
116 * 1683: function array_merge($arr1,$arr2)
117 * 1696: function csvValues($row,$delim=',',$quote='"')
118 *
119 * SECTION: HTML/XML PROCESSING
120 * 1738: function get_tag_attributes($tag)
121 * 1775: function split_tag_attributes($tag)
122 * 1809: function implodeAttributes($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)
123 * 1836: function implodeParams($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)
124 * 1851: function wrapJS($string, $linebreak=TRUE)
125 * 1882: function xml2tree($string,$depth=999)
126 * 1969: function array2xml($array,$NSprefix='',$level=0,$docTag='phparray',$spaceInd=0, $options=array(),$stackData=array())
127 * 2088: function xml2array($string,$NSprefix='',$reportDocTag=FALSE)
128 * 2198: function xmlRecompileFromStructValArray($vals)
129 * 2242: function xmlGetHeaderAttribs($xmlData)
130 *
131 * SECTION: FILES FUNCTIONS
132 * 2275: function getURL($url, $includeHeader=0)
133 * 2342: function writeFile($file,$content)
134 * 2367: function fixPermissions($file)
135 * 2384: function writeFileToTypo3tempDir($filepath,$content)
136 * 2427: function mkdir($theNewFolder)
137 * 2446: function mkdir_deep($destination,$deepDir)
138 * 2468: function get_dirs($path)
139 * 2493: function getFilesInDir($path,$extensionList='',$prependPath=0,$order='')
140 * 2547: function getAllFilesAndFoldersInPath($fileArr,$path,$extList='',$regDirs=0,$recursivityLevels=99)
141 * 2570: function removePrefixPathFromList($fileArr,$prefixToRemove)
142 * 2586: function fixWindowsFilePath($theFile)
143 * 2598: function resolveBackPath($pathStr)
144 * 2626: function locationHeaderUrl($path)
145 *
146 * SECTION: DEBUG helper FUNCTIONS
147 * 2666: function debug_ordvalue($string,$characters=100)
148 * 2683: function view_array($array_in)
149 * 2711: function print_array($array_in)
150 * 2726: function debug($var="",$brOrHeader=0)
151 * 2757: function debug_trail()
152 * 2779: function debugRows($rows,$header='')
153 *
154 * SECTION: SYSTEM INFORMATION
155 * 2857: function getThisUrl()
156 * 2873: function linkThisScript($getParams=array())
157 * 2897: function linkThisUrl($url,$getParams=array())
158 * 2920: function getIndpEnv($getEnvName)
159 * 3113: function milliseconds()
160 * 3125: function clientInfo($useragent='')
161 *
162 * SECTION: TYPO3 SPECIFIC FUNCTIONS
163 * 3212: function getFileAbsFileName($filename,$onlyRelative=1,$relToTYPO3_mainDir=0)
164 * 3248: function validPathStr($theFile)
165 * 3259: function isAbsPath($path)
166 * 3270: function isAllowedAbsPath($path)
167 * 3287: function verifyFilenameAgainstDenyPattern($filename)
168 * 3305: function upload_copy_move($source,$destination)
169 * 3331: function upload_to_tempfile($uploadedFileName)
170 * 3349: function unlink_tempfile($uploadedTempFileName)
171 * 3365: function tempnam($filePrefix)
172 * 3379: function stdAuthCode($uid_or_record,$fields='',$codeLength=8)
173 * 3410: function cHashParams($addQueryParams)
174 * 3433: function hideIfNotTranslated($l18n_cfg_fieldValue)
175 * 3448: function readLLfile($fileRef,$langKey)
176 * 3472: function readLLXMLfile($fileRef,$langKey)
177 * 3589: function llXmlAutoFileName($fileRef,$language)
178 * 3633: function loadTCA($table)
179 * 3653: function resolveSheetDefInDS($dataStructArray,$sheet='sDEF')
180 * 3686: function resolveAllSheetsInDS($dataStructArray)
181 * 3715: function callUserFunction($funcName,&$params,&$ref,$checkPrefix='user_',$silent=0)
182 * 3813: function &getUserObj($classRef,$checkPrefix='user_',$silent=0)
183 * 3871: function &makeInstance($className)
184 * 3883: function makeInstanceClassName($className)
185 * 3897: function &makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array())
186 * 3961: function plainMailEncoded($email,$subject,$message,$headers='',$enc='',$charset='',$dontEncodeHeader=false)
187 * 4031: function quoted_printable($string,$maxlen=76)
188 * 4078: function encodeHeader($line,$enc='',$charset='ISO-8859-1')
189 * 4121: function substUrlsInPlainText($message,$urlmode='76',$index_script_url='')
190 * 4155: function makeRedirectUrl($inUrl,$l=0,$index_script_url='')
191 * 4182: function freetypeDpiComp($font_size)
192 * 4194: function initSysLog()
193 * 4251: function sysLog($msg, $extKey, $severity=0)
194 * 4334: function devLog($msg, $extKey, $severity=0, $dataVar=FALSE)
195 * 4355: function arrayToLogString($arr, $valueList=array(), $valueLength=20)
196 * 4378: function imageMagickCommand($command, $parameters, $path='')
197 * 4425: function unQuoteFilenames($parameters,$unQuote=FALSE)
198 * 4459: function quoteJSvalue($value, $inScriptTags = false)
199 *
200 * TOTAL FUNCTIONS: 138
201 * (This index is automatically created/updated by the extension "extdeveval")
202 *
203 */
204
205 // a tabulator
206 define('TAB', chr(9));
207 // a linefeed
208 define('LF', chr(10));
209 // a carriage return
210 define('CR', chr(13));
211 // a CR-LF combination
212 define('CRLF', CR . LF);
213
214 /**
215 * The legendary "t3lib_div" class - Miscellaneous functions for general purpose.
216 * Most of the functions does not relate specifically to TYPO3
217 * However a section of functions requires certain TYPO3 features available
218 * See comments in the source.
219 * You are encouraged to use this library in your own scripts!
220 *
221 * USE:
222 * The class is intended to be used without creating an instance of it.
223 * So: Don't instantiate - call functions with "t3lib_div::" prefixed the function name.
224 * So use t3lib_div::[method-name] to refer to the functions, eg. 't3lib_div::milliseconds()'
225 *
226 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
227 * @package TYPO3
228 * @subpackage t3lib
229 */
230 final class t3lib_div {
231
232 // Severity constants used by t3lib_div::sysLog()
233 const SYSLOG_SEVERITY_INFO = 0;
234 const SYSLOG_SEVERITY_NOTICE = 1;
235 const SYSLOG_SEVERITY_WARNING = 2;
236 const SYSLOG_SEVERITY_ERROR = 3;
237 const SYSLOG_SEVERITY_FATAL = 4;
238
239 /**
240 * Singleton instances returned by makeInstance, using the class names as
241 * array keys
242 *
243 * @var array<t3lib_Singleton>
244 */
245 protected static $singletonInstances = array();
246
247 /**
248 * Instances returned by makeInstance, using the class names as array keys
249 *
250 * @var array<array><object>
251 */
252 protected static $nonSingletonInstances = array();
253
254 /**
255 * Register for makeInstance with given class name and final class names to reduce number of class_exists() calls
256 *
257 * @var array Given class name => final class name
258 */
259 protected static $finalClassNameRegister = array();
260
261 /*************************
262 *
263 * GET/POST Variables
264 *
265 * Background:
266 * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
267 * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
268 * But the clean solution is that quotes are never escaped and that is what the functions below offers.
269 * Eventually TYPO3 should provide this in the global space as well.
270 * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
271 *
272 *************************/
273
274 /**
275 * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order)
276 * Strips slashes from all output, both strings and arrays.
277 * This function substitutes t3lib_div::GPvar()
278 * To enhancement security in your scripts, please consider using t3lib_div::_GET or t3lib_div::_POST if you already know by which method your data is arriving to the scripts!
279 * Usage: 537
280 *
281 * @param string GET/POST var to return
282 * @return mixed POST var named $var and if not set, the GET var of the same name.
283 */
284 public static function _GP($var) {
285 if (empty($var)) {
286 return;
287 }
288 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
289 if (isset($value)) {
290 if (is_array($value)) {
291 self::stripSlashesOnArray($value);
292 } else {
293 $value = stripslashes($value);
294 }
295 }
296 return $value;
297 }
298
299 /**
300 * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence.
301 *
302 * @param string Key (variable name) from GET or POST vars
303 * @return array Returns the GET vars merged recursively onto the POST vars.
304 */
305 public static function _GPmerged($parameter) {
306 $postParameter = (isset($_POST[$parameter]) && is_array($_POST[$parameter])) ? $_POST[$parameter] : array();
307 $getParameter = (isset($_GET[$parameter]) && is_array($_GET[$parameter])) ? $_GET[$parameter] : array();
308
309 $mergedParameters = self::array_merge_recursive_overrule($getParameter, $postParameter);
310 self::stripSlashesOnArray($mergedParameters);
311
312 return $mergedParameters;
313 }
314
315 /**
316 * Returns the global $_GET array (or value from) normalized to contain un-escaped values.
317 * ALWAYS use this API function to acquire the GET variables!
318 * Usage: 27
319 *
320 * @param string Optional pointer to value in GET array (basically name of GET var)
321 * @return mixed If $var is set it returns the value of $_GET[$var]. If $var is NULL (default), returns $_GET itself. In any case *slashes are stipped from the output!*
322 * @see _POST(), _GP(), _GETset()
323 */
324 public static function _GET($var = NULL) {
325 $value = ($var === NULL) ? $_GET : (empty($var) ? NULL : $_GET[$var]);
326 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
327 if (is_array($value)) {
328 self::stripSlashesOnArray($value);
329 } else {
330 $value = stripslashes($value);
331 }
332 }
333 return $value;
334 }
335
336 /**
337 * Returns the global $_POST array (or value from) normalized to contain un-escaped values.
338 * ALWAYS use this API function to acquire the $_POST variables!
339 * Usage: 41
340 *
341 * @param string Optional pointer to value in POST array (basically name of POST var)
342 * @return mixed If $var is set it returns the value of $_POST[$var]. If $var is NULL (default), returns $_POST itself. In any case *slashes are stipped from the output!*
343 * @see _GET(), _GP()
344 */
345 public static function _POST($var = NULL) {
346 $value = ($var === NULL) ? $_POST : (empty($var) ? NULL : $_POST[$var]);
347 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
348 if (is_array($value)) {
349 self::stripSlashesOnArray($value);
350 } else {
351 $value = stripslashes($value);
352 }
353 }
354 return $value;
355 }
356
357 /**
358 * Writes input value to $_GET.
359 * Usage: 2
360 *
361 * @param mixed $inputGet
362 * array or single value to write to $_GET. Values should NOT be
363 * escaped at input time (but will be escaped before writing
364 * according to TYPO3 standards).
365 * @param string $key
366 * alternative key; If set, this will not set the WHOLE GET array,
367 * but only the key in it specified by this value!
368 * You can specify to replace keys on deeper array levels by
369 * separating the keys with a pipe.
370 * Example: 'parentKey|childKey' will result in
371 * array('parentKey' => array('childKey' => $inputGet))
372 *
373 * @return void
374 */
375 public static function _GETset($inputGet, $key = '') {
376 // adds slashes since TYPO3 standard currently is that slashes
377 // must be applied (regardless of magic_quotes setting)
378 if (is_array($inputGet)) {
379 self::addSlashesOnArray($inputGet);
380 } else {
381 $inputGet = addslashes($inputGet);
382 }
383
384 if ($key != '') {
385 if (strpos($key, '|') !== FALSE) {
386 $pieces = explode('|', $key);
387 $newGet = array();
388 $pointer =& $newGet;
389 foreach ($pieces as $piece) {
390 $pointer =& $pointer[$piece];
391 }
392 $pointer = $inputGet;
393 $mergedGet = self::array_merge_recursive_overrule(
394 $_GET, $newGet
395 );
396
397 $_GET = $mergedGet;
398 $GLOBALS['HTTP_GET_VARS'] = $mergedGet;
399 } else {
400 $_GET[$key] = $inputGet;
401 $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet;
402 }
403 } elseif (is_array($inputGet)) {
404 $_GET = $inputGet;
405 $GLOBALS['HTTP_GET_VARS'] = $inputGet;
406 }
407 }
408
409 /**
410 * Returns the value of incoming data from globals variable $_POST or $_GET, with priority to $_POST (that is equalent to 'GP' order).
411 * Strips slashes of string-outputs, but not arrays UNLESS $strip is set. If $strip is set all output will have escaped characters unescaped.
412 * Usage: 2
413 *
414 * @param string GET/POST var to return
415 * @param boolean If set, values are stripped of return values that are *arrays!* - string/integer values returned are always strip-slashed()
416 * @return mixed POST var named $var and if not set, the GET var of the same name.
417 * @deprecated since TYPO3 3.6, will be removed in TYPO3 4.6 - Use t3lib_div::_GP instead (ALWAYS delivers a value with un-escaped values!)
418 * @see _GP()
419 */
420 public static function GPvar($var, $strip = 0) {
421 self::logDeprecatedFunction();
422
423 if (empty($var)) {
424 return;
425 }
426 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
427 if (isset($value) && is_string($value)) {
428 $value = stripslashes($value);
429 } // Originally check '&& get_magic_quotes_gpc() ' but the values of $_GET are always slashed regardless of get_magic_quotes_gpc() because HTTP_POST/GET_VARS are run through addSlashesOnArray in the very beginning of index_ts.php eg.
430 if ($strip && isset($value) && is_array($value)) {
431 self::stripSlashesOnArray($value);
432 }
433 return $value;
434 }
435
436 /**
437 * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence.
438 * Usage: 1
439 *
440 * @param string Key (variable name) from GET or POST vars
441 * @return array Returns the GET vars merged recursively onto the POST vars.
442 * @deprecated since TYPO3 3.7, will be removed in TYPO3 4.6 - Use t3lib_div::_GPmerged instead
443 * @see _GP()
444 */
445 public static function GParrayMerged($var) {
446 self::logDeprecatedFunction();
447
448 return self::_GPmerged($var);
449 }
450
451 /**
452 * Wrapper for the RemoveXSS function.
453 * Removes potential XSS code from an input string.
454 *
455 * Using an external class by Travis Puderbaugh <kallahar@quickwired.com>
456 *
457 * @param string Input string
458 * @return string Input string with potential XSS code removed
459 */
460 public static function removeXSS($string) {
461 require_once(PATH_typo3 . 'contrib/RemoveXSS/RemoveXSS.php');
462 $string = RemoveXSS::process($string);
463 return $string;
464 }
465
466
467 /*************************
468 *
469 * IMAGE FUNCTIONS
470 *
471 *************************/
472
473
474 /**
475 * Compressing a GIF file if not already LZW compressed.
476 * This function is a workaround for the fact that ImageMagick and/or GD does not compress GIF-files to their minimun size (that is RLE or no compression used)
477 *
478 * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file
479 * GIF:
480 * If $type is not set, the compression is done with ImageMagick (provided that $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw'] is pointing to the path of a lzw-enabled version of 'convert') else with GD (should be RLE-enabled!)
481 * If $type is set to either 'IM' or 'GD' the compression is done with ImageMagick and GD respectively
482 * PNG:
483 * No changes.
484 *
485 * $theFile is expected to be a valid GIF-file!
486 * The function returns a code for the operation.
487 * Usage: 9
488 *
489 * @param string Filepath
490 * @param string See description of function
491 * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string.
492 */
493 public static function gif_compress($theFile, $type) {
494 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
495 $returnCode = '';
496 if ($gfxConf['gif_compress'] && strtolower(substr($theFile, -4, 4)) == '.gif') { // GIF...
497 if (($type == 'IM' || !$type) && $gfxConf['im'] && $gfxConf['im_path_lzw']) { // IM
498 // use temporary file to prevent problems with read and write lock on same file on network file systems
499 $temporaryName = dirname($theFile) . '/' . md5(uniqid()) . '.gif';
500 // rename could fail, if a simultaneous thread is currently working on the same thing
501 if (@rename($theFile, $temporaryName)) {
502 $cmd = self::imageMagickCommand('convert', '"' . $temporaryName . '" "' . $theFile . '"', $gfxConf['im_path_lzw']);
503 t3lib_utility_Command::exec($cmd);
504 unlink($temporaryName);
505 }
506
507 $returnCode = 'IM';
508 if (@is_file($theFile)) {
509 self::fixPermissions($theFile);
510 }
511 } elseif (($type == 'GD' || !$type) && $gfxConf['gdlib'] && !$gfxConf['gdlib_png']) { // GD
512 $tempImage = imageCreateFromGif($theFile);
513 imageGif($tempImage, $theFile);
514 imageDestroy($tempImage);
515 $returnCode = 'GD';
516 if (@is_file($theFile)) {
517 self::fixPermissions($theFile);
518 }
519 }
520 }
521 return $returnCode;
522 }
523
524 /**
525 * Converts a png file to gif.
526 * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set true.
527 * Usage: 5
528 *
529 * @param string $theFile the filename with path
530 * @return string new filename
531 */
532 public static function png_to_gif_by_imagemagick($theFile) {
533 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif']
534 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im']
535 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']
536 && strtolower(substr($theFile, -4, 4)) == '.png'
537 && @is_file($theFile)) { // IM
538 $newFile = substr($theFile, 0, -4) . '.gif';
539 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']);
540 t3lib_utility_Command::exec($cmd);
541 $theFile = $newFile;
542 if (@is_file($newFile)) {
543 self::fixPermissions($newFile);
544 }
545 // unlink old file?? May be bad idea bacause TYPO3 would then recreate the file every time as TYPO3 thinks the file is not generated because it's missing!! So do not unlink $theFile here!!
546 }
547 return $theFile;
548 }
549
550 /**
551 * Returns filename of the png/gif version of the input file (which can be png or gif).
552 * If input file type does not match the wanted output type a conversion is made and temp-filename returned.
553 * Usage: 2
554 *
555 * @param string Filepath of image file
556 * @param boolean If set, then input file is converted to PNG, otherwise to GIF
557 * @return string If the new image file exists, it's filepath is returned
558 */
559 public static function read_png_gif($theFile, $output_png = 0) {
560 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] && @is_file($theFile)) {
561 $ext = strtolower(substr($theFile, -4, 4));
562 if (
563 ((string) $ext == '.png' && $output_png) ||
564 ((string) $ext == '.gif' && !$output_png)
565 ) {
566 return $theFile;
567 } else {
568 $newFile = PATH_site . 'typo3temp/readPG_' . md5($theFile . '|' . filemtime($theFile)) . ($output_png ? '.png' : '.gif');
569 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path']);
570 t3lib_utility_Command::exec($cmd);
571 if (@is_file($newFile)) {
572 self::fixPermissions($newFile);
573 return $newFile;
574 }
575 }
576 }
577 }
578
579
580 /*************************
581 *
582 * STRING FUNCTIONS
583 *
584 *************************/
585
586 /**
587 * Truncates string.
588 * Returns a new string of max. $chars length.
589 * If the string is longer, it will be truncated and appended with '...'.
590 * Usage: 39
591 *
592 * @param string string to truncate
593 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
594 * @param string String to append to the output if it is truncated, default is '...'
595 * @return string new string
596 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Works ONLY for single-byte charsets! Use t3lib_div::fixed_lgd_cs() instead
597 * @see fixed_lgd_pre()
598 */
599 public static function fixed_lgd($string, $origChars, $preStr = '...') {
600 self::logDeprecatedFunction();
601
602 $chars = abs($origChars);
603 if ($chars >= 4) {
604 if (strlen($string) > $chars) {
605 return $origChars < 0 ?
606 $preStr . trim(substr($string, -($chars - 3))) :
607 trim(substr($string, 0, $chars - 3)) . $preStr;
608 }
609 }
610 return $string;
611 }
612
613 /**
614 * Truncates string.
615 * Returns a new string of max. $chars length.
616 * If the string is longer, it will be truncated and prepended with '...'.
617 * This works like fixed_lgd(), but is truncated in the start of the string instead of the end
618 * Usage: 6
619 *
620 * @param string string to truncate
621 * @param integer must be an integer of at least 4
622 * @return string new string
623 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Use t3lib_div::fixed_lgd_cs() instead (with negative input value for $chars)
624 * @see fixed_lgd()
625 */
626 public static function fixed_lgd_pre($string, $chars) {
627 self::logDeprecatedFunction();
628
629 return strrev(self::fixed_lgd(strrev($string), $chars));
630 }
631
632 /**
633 * Truncates a string with appended/prepended "..." and takes current character set into consideration.
634 * Usage: 75
635 *
636 * @param string string to truncate
637 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
638 * @param string appendix to the truncated string
639 * @return string cropped string
640 */
641 public static function fixed_lgd_cs($string, $chars, $appendString = '...') {
642 if (is_object($GLOBALS['LANG'])) {
643 return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString);
644 } elseif (is_object($GLOBALS['TSFE'])) {
645 $charSet = ($GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet);
646 return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString);
647 } else {
648 // this case should not happen
649 $csConvObj = self::makeInstance('t3lib_cs');
650 return $csConvObj->crop('iso-8859-1', $string, $chars, $appendString);
651 }
652 }
653
654 /**
655 * Breaks up the text for emails
656 * Usage: 1
657 *
658 * @param string The string to break up
659 * @param string The string to implode the broken lines with (default/typically \n)
660 * @param integer The line length
661 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Use PHP function wordwrap()
662 * @return string
663 */
664 public static function breakTextForEmail($str, $implChar = LF, $charWidth = 76) {
665 self::logDeprecatedFunction();
666
667 $lines = explode(LF, $str);
668 $outArr = array();
669 foreach ($lines as $lStr) {
670 $outArr[] = self::breakLinesForEmail($lStr, $implChar, $charWidth);
671 }
672 return implode(LF, $outArr);
673 }
674
675 /**
676 * Breaks up a single line of text for emails
677 * Usage: 5
678 *
679 * @param string The string to break up
680 * @param string The string to implode the broken lines with (default/typically \n)
681 * @param integer The line length
682 * @return string
683 * @see breakTextForEmail()
684 */
685 public static function breakLinesForEmail($str, $implChar = LF, $charWidth = 76) {
686 $lines = array();
687 $l = $charWidth;
688 $p = 0;
689 while (strlen($str) > $p) {
690 $substr = substr($str, $p, $l);
691 if (strlen($substr) == $l) {
692 $count = count(explode(' ', trim(strrev($substr))));
693 if ($count > 1) { // OK...
694 $parts = explode(' ', strrev($substr), 2);
695 $theLine = strrev($parts[1]);
696 } else {
697 $afterParts = explode(' ', substr($str, $l + $p), 2);
698 $theLine = $substr . $afterParts[0];
699 }
700 if (!strlen($theLine)) {
701 break;
702 } // Error, because this would keep us in an endless loop.
703 } else {
704 $theLine = $substr;
705 }
706
707 $lines[] = trim($theLine);
708 $p += strlen($theLine);
709 if (!trim(substr($str, $p, $l))) {
710 break;
711 } // added...
712 }
713 return implode($implChar, $lines);
714 }
715
716 /**
717 * Match IP number with list of numbers with wildcard
718 * Dispatcher method for switching into specialised IPv4 and IPv6 methods.
719 * Usage: 10
720 *
721 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
722 * @param string $list is a comma-list of IP-addresses to match with. *-wildcard allowed instead of number, plus leaving out parts in the IP number is accepted as wildcard (eg. 192.168.*.* equals 192.168). If list is "*" no check is done and the function returns TRUE immediately. An empty list always returns FALSE.
723 * @return boolean True if an IP-mask from $list matches $baseIP
724 */
725 public static function cmpIP($baseIP, $list) {
726 $list = trim($list);
727 if ($list === '') {
728 return FALSE;
729 } elseif ($list === '*') {
730 return TRUE;
731 }
732 if (strpos($baseIP, ':') !== FALSE && self::validIPv6($baseIP)) {
733 return self::cmpIPv6($baseIP, $list);
734 } else {
735 return self::cmpIPv4($baseIP, $list);
736 }
737 }
738
739 /**
740 * Match IPv4 number with list of numbers with wildcard
741 *
742 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
743 * @param string $list is a comma-list of IP-addresses to match with. *-wildcard allowed instead of number, plus leaving out parts in the IP number is accepted as wildcard (eg. 192.168.*.* equals 192.168)
744 * @return boolean True if an IP-mask from $list matches $baseIP
745 */
746 public static function cmpIPv4($baseIP, $list) {
747 $IPpartsReq = explode('.', $baseIP);
748 if (count($IPpartsReq) == 4) {
749 $values = self::trimExplode(',', $list, 1);
750
751 foreach ($values as $test) {
752 list($test, $mask) = explode('/', $test);
753
754 if (intval($mask)) {
755 // "192.168.3.0/24"
756 $lnet = ip2long($test);
757 $lip = ip2long($baseIP);
758 $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
759 $firstpart = substr($binnet, 0, $mask);
760 $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
761 $firstip = substr($binip, 0, $mask);
762 $yes = (strcmp($firstpart, $firstip) == 0);
763 } else {
764 // "192.168.*.*"
765 $IPparts = explode('.', $test);
766 $yes = 1;
767 foreach ($IPparts as $index => $val) {
768 $val = trim($val);
769 if (strcmp($val, '*') && strcmp($IPpartsReq[$index], $val)) {
770 $yes = 0;
771 }
772 }
773 }
774 if ($yes) {
775 return TRUE;
776 }
777 }
778 }
779 return FALSE;
780 }
781
782 /**
783 * Match IPv6 address with a list of IPv6 prefixes
784 *
785 * @param string $baseIP is the current remote IP address for instance
786 * @param string $list is a comma-list of IPv6 prefixes, could also contain IPv4 addresses
787 * @return boolean True if an baseIP matches any prefix
788 */
789 public static function cmpIPv6($baseIP, $list) {
790 $success = FALSE; // Policy default: Deny connection
791 $baseIP = self::normalizeIPv6($baseIP);
792
793 $values = self::trimExplode(',', $list, 1);
794 foreach ($values as $test) {
795 list($test, $mask) = explode('/', $test);
796 if (self::validIPv6($test)) {
797 $test = self::normalizeIPv6($test);
798 if (intval($mask)) {
799 switch ($mask) { // test on /48 /64
800 case '48':
801 $testBin = substr(self::IPv6Hex2Bin($test), 0, 48);
802 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 48);
803 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
804 break;
805 case '64':
806 $testBin = substr(self::IPv6Hex2Bin($test), 0, 64);
807 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 64);
808 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
809 break;
810 default:
811 $success = FALSE;
812 }
813 } else {
814 if (self::validIPv6($test)) { // test on full ip address 128 bits
815 $testBin = self::IPv6Hex2Bin($test);
816 $baseIPBin = self::IPv6Hex2Bin($baseIP);
817 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
818 }
819 }
820 }
821 if ($success) {
822 return TRUE;
823 }
824 }
825 return FALSE;
826 }
827
828 /**
829 * [Describe function...]
830 *
831 * @param [type] $hex: ...
832 * @return [type] ...
833 */
834 public static function IPv6Hex2Bin($hex) {
835 $bin = '';
836 $hex = str_replace(':', '', $hex); // Replace colon to nothing
837 for ($i = 0; $i < strlen($hex); $i = $i + 2) {
838 $bin .= chr(hexdec(substr($hex, $i, 2)));
839 }
840 return $bin;
841 }
842
843 /**
844 * Normalize an IPv6 address to full length
845 *
846 * @param string Given IPv6 address
847 * @return string Normalized address
848 */
849 public static function normalizeIPv6($address) {
850 $normalizedAddress = '';
851 $stageOneAddress = '';
852
853 $chunks = explode('::', $address); // Count 2 if if address has hidden zero blocks
854 if (count($chunks) == 2) {
855 $chunksLeft = explode(':', $chunks[0]);
856 $chunksRight = explode(':', $chunks[1]);
857 $left = count($chunksLeft);
858 $right = count($chunksRight);
859
860 // Special case: leading zero-only blocks count to 1, should be 0
861 if ($left == 1 && strlen($chunksLeft[0]) == 0) {
862 $left = 0;
863 }
864
865 $hiddenBlocks = 8 - ($left + $right);
866 $hiddenPart = '';
867 $h = 0;
868 while ($h < $hiddenBlocks) {
869 $hiddenPart .= '0000:';
870 $h++;
871 }
872
873 if ($left == 0) {
874 $stageOneAddress = $hiddenPart . $chunks[1];
875 } else {
876 $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
877 }
878 } else {
879 $stageOneAddress = $address;
880 }
881
882 // normalize the blocks:
883 $blocks = explode(':', $stageOneAddress);
884 $divCounter = 0;
885 foreach ($blocks as $block) {
886 $tmpBlock = '';
887 $i = 0;
888 $hiddenZeros = 4 - strlen($block);
889 while ($i < $hiddenZeros) {
890 $tmpBlock .= '0';
891 $i++;
892 }
893 $normalizedAddress .= $tmpBlock . $block;
894 if ($divCounter < 7) {
895 $normalizedAddress .= ':';
896 $divCounter++;
897 }
898 }
899 return $normalizedAddress;
900 }
901
902 /**
903 * Validate a given IP address.
904 *
905 * Possible format are IPv4 and IPv6.
906 *
907 * @param string IP address to be tested
908 * @return boolean True if $ip is either of IPv4 or IPv6 format.
909 */
910 public static function validIP($ip) {
911 return (filter_var($ip, FILTER_VALIDATE_IP) !== FALSE);
912 }
913
914 /**
915 * Validate a given IP address to the IPv4 address format.
916 *
917 * Example for possible format: 10.0.45.99
918 *
919 * @param string IP address to be tested
920 * @return boolean True if $ip is of IPv4 format.
921 */
922 public static function validIPv4($ip) {
923 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE);
924 }
925
926 /**
927 * Validate a given IP address to the IPv6 address format.
928 *
929 * Example for possible format: 43FB::BB3F:A0A0:0 | ::1
930 *
931 * @param string IP address to be tested
932 * @return boolean True if $ip is of IPv6 format.
933 */
934 public static function validIPv6($ip) {
935 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE);
936 }
937
938 /**
939 * Match fully qualified domain name with list of strings with wildcard
940 *
941 * @param string The current remote IP address for instance, typ. REMOTE_ADDR
942 * @param string A comma-list of domain names to match with. *-wildcard allowed but cannot be part of a string, so it must match the full host name (eg. myhost.*.com => correct, myhost.*domain.com => wrong)
943 * @return boolean True if a domain name mask from $list matches $baseIP
944 */
945 public static function cmpFQDN($baseIP, $list) {
946 if (count(explode('.', $baseIP)) == 4) {
947 $resolvedHostName = explode('.', gethostbyaddr($baseIP));
948 $values = self::trimExplode(',', $list, 1);
949
950 foreach ($values as $test) {
951 $hostNameParts = explode('.', $test);
952 $yes = 1;
953
954 foreach ($hostNameParts as $index => $val) {
955 $val = trim($val);
956 if (strcmp($val, '*') && strcmp($resolvedHostName[$index], $val)) {
957 $yes = 0;
958 }
959 }
960 if ($yes) {
961 return TRUE;
962 }
963 }
964 }
965 return FALSE;
966 }
967
968 /**
969 * Checks if a given URL matches the host that currently handles this HTTP request.
970 * Scheme, hostname and (optional) port of the given URL are compared.
971 *
972 * @param string $url: URL to compare with the TYPO3 request host
973 * @return boolean Whether the URL matches the TYPO3 request host
974 */
975 public static function isOnCurrentHost($url) {
976 return (stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0);
977 }
978
979 /**
980 * Check for item in list
981 * Check if an item exists in a comma-separated list of items.
982 * Usage: 163
983 *
984 * @param string comma-separated list of items (string)
985 * @param string item to check for
986 * @return boolean true if $item is in $list
987 */
988 public static function inList($list, $item) {
989 return (strpos(',' . $list . ',', ',' . $item . ',') !== FALSE ? TRUE : FALSE);
990 }
991
992 /**
993 * Removes an item from a comma-separated list of items.
994 * Usage: 1
995 *
996 * @param string element to remove
997 * @param string comma-separated list of items (string)
998 * @return string new comma-separated list of items
999 */
1000 public static function rmFromList($element, $list) {
1001 $items = explode(',', $list);
1002 foreach ($items as $k => $v) {
1003 if ($v == $element) {
1004 unset($items[$k]);
1005 }
1006 }
1007 return implode(',', $items);
1008 }
1009
1010 /**
1011 * Expand a comma-separated list of integers with ranges (eg 1,3-5,7 becomes 1,3,4,5,7).
1012 * Ranges are limited to 1000 values per range.
1013 *
1014 * @param string comma-separated list of integers with ranges (string)
1015 * @return string new comma-separated list of items
1016 * @author Martin Kutschker <martin.kutschker@activesolution.at>
1017 */
1018 public static function expandList($list) {
1019 $items = explode(',', $list);
1020 $list = array();
1021 foreach ($items as $item) {
1022 $range = explode('-', $item);
1023 if (isset($range[1])) {
1024 $runAwayBrake = 1000;
1025 for ($n = $range[0]; $n <= $range[1]; $n++) {
1026 $list[] = $n;
1027
1028 $runAwayBrake--;
1029 if ($runAwayBrake <= 0) {
1030 break;
1031 }
1032 }
1033 } else {
1034 $list[] = $item;
1035 }
1036 }
1037 return implode(',', $list);
1038 }
1039
1040 /**
1041 * Forces the integer $theInt into the boundaries of $min and $max. If the $theInt is 'false' then the $zeroValue is applied.
1042 * Usage: 224
1043 *
1044 * @param integer Input value
1045 * @param integer Lower limit
1046 * @param integer Higher limit
1047 * @param integer Default value if input is false.
1048 * @return integer The input value forced into the boundaries of $min and $max
1049 */
1050 public static function intInRange($theInt, $min, $max = 2000000000, $zeroValue = 0) {
1051 // Returns $theInt as an integer in the integerspace from $min to $max
1052 $theInt = intval($theInt);
1053 if ($zeroValue && !$theInt) {
1054 $theInt = $zeroValue;
1055 } // If the input value is zero after being converted to integer, zeroValue may set another default value for it.
1056 if ($theInt < $min) {
1057 $theInt = $min;
1058 }
1059 if ($theInt > $max) {
1060 $theInt = $max;
1061 }
1062 return $theInt;
1063 }
1064
1065 /**
1066 * Returns the $integer if greater than zero, otherwise returns zero.
1067 * Usage: 1
1068 *
1069 * @param integer Integer string to process
1070 * @return integer
1071 */
1072 public static function intval_positive($theInt) {
1073 $theInt = intval($theInt);
1074 if ($theInt < 0) {
1075 $theInt = 0;
1076 }
1077 return $theInt;
1078 }
1079
1080 /**
1081 * Returns an integer from a three part version number, eg '4.12.3' -> 4012003
1082 * Usage: 2
1083 *
1084 * @param string Version number on format x.x.x
1085 * @return integer Integer version of version number (where each part can count to 999)
1086 */
1087 public static function int_from_ver($verNumberStr) {
1088 $verParts = explode('.', $verNumberStr);
1089 return intval((int) $verParts[0] . str_pad((int) $verParts[1], 3, '0', STR_PAD_LEFT) . str_pad((int) $verParts[2], 3, '0', STR_PAD_LEFT));
1090 }
1091
1092 /**
1093 * Returns true if the current TYPO3 version (or compatibility version) is compatible to the input version
1094 * Notice that this function compares branches, not versions (4.0.1 would be > 4.0.0 although they use the same compat_version)
1095 *
1096 * @param string Minimum branch number required (format x.y / e.g. "4.0" NOT "4.0.0"!)
1097 * @return boolean Returns true if this setup is compatible with the provided version number
1098 * @todo Still needs a function to convert versions to branches
1099 */
1100 public static function compat_version($verNumberStr) {
1101 global $TYPO3_CONF_VARS;
1102 $currVersionStr = $TYPO3_CONF_VARS['SYS']['compat_version'] ? $TYPO3_CONF_VARS['SYS']['compat_version'] : TYPO3_branch;
1103
1104 if (self::int_from_ver($currVersionStr) < self::int_from_ver($verNumberStr)) {
1105 return FALSE;
1106 } else {
1107 return TRUE;
1108 }
1109 }
1110
1111 /**
1112 * Makes a positive integer hash out of the first 7 chars from the md5 hash of the input
1113 * Usage: 5
1114 *
1115 * @param string String to md5-hash
1116 * @return integer Returns 28bit integer-hash
1117 */
1118 public static function md5int($str) {
1119 return hexdec(substr(md5($str), 0, 7));
1120 }
1121
1122 /**
1123 * Returns the first 10 positions of the MD5-hash (changed from 6 to 10 recently)
1124 *
1125 * Usage: 37
1126 *
1127 * @param string Input string to be md5-hashed
1128 * @param integer The string-length of the output
1129 * @return string Substring of the resulting md5-hash, being $len chars long (from beginning)
1130 */
1131 public static function shortMD5($input, $len = 10) {
1132 return substr(md5($input), 0, $len);
1133 }
1134
1135 /**
1136 * Returns a proper HMAC on a given input string and secret TYPO3 encryption key.
1137 *
1138 * @param string Input string to create HMAC from
1139 * @return string resulting (hexadecimal) HMAC currently with a length of 40 (HMAC-SHA-1)
1140 */
1141 public static function hmac($input) {
1142 $hashAlgorithm = 'sha1';
1143 $hashBlocksize = 64;
1144 $hmac = '';
1145
1146 if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
1147 $hmac = hash_hmac($hashAlgorithm, $input, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
1148 } else {
1149 // outer padding
1150 $opad = str_repeat(chr(0x5C), $hashBlocksize);
1151 // innner padding
1152 $ipad = str_repeat(chr(0x36), $hashBlocksize);
1153 if (strlen($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) > $hashBlocksize) {
1154 // keys longer than blocksize are shorten
1155 $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])), $hashBlocksize, chr(0x00));
1156 } else {
1157 // keys shorter than blocksize are zero-padded
1158 $key = str_pad($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $hashBlocksize, chr(0x00));
1159 }
1160 $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, ($key ^ $ipad) . $input)));
1161 }
1162 return $hmac;
1163 }
1164
1165 /**
1166 * Takes comma-separated lists and arrays and removes all duplicates
1167 * If a value in the list is trim(empty), the value is ignored.
1168 * Usage: 16
1169 *
1170 * @param string Accept multiple parameters wich can be comma-separated lists of values and arrays.
1171 * @param mixed $secondParameter: Dummy field, which if set will show a warning!
1172 * @return string Returns the list without any duplicates of values, space around values are trimmed
1173 */
1174 public static function uniqueList($in_list, $secondParameter = NULL) {
1175 if (is_array($in_list)) {
1176 throw new InvalidArgumentException(
1177 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support array arguments anymore! Only string comma lists!',
1178 1270853885
1179 );
1180 }
1181 if (isset($secondParameter)) {
1182 throw new InvalidArgumentException(
1183 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one!',
1184 1270853886
1185 );
1186 }
1187
1188 return implode(',', array_unique(self::trimExplode(',', $in_list, 1)));
1189 }
1190
1191 /**
1192 * Splits a reference to a file in 5 parts
1193 * Usage: 43
1194 *
1195 * @param string Filename/filepath to be analysed
1196 * @return array Contains keys [path], [file], [filebody], [fileext], [realFileext]
1197 */
1198 public static function split_fileref($fileref) {
1199 $reg = array();
1200 if (preg_match('/(.*\/)(.*)$/', $fileref, $reg)) {
1201 $info['path'] = $reg[1];
1202 $info['file'] = $reg[2];
1203 } else {
1204 $info['path'] = '';
1205 $info['file'] = $fileref;
1206 }
1207
1208 $reg = '';
1209 if (!is_dir($fileref) && preg_match('/(.*)\.([^\.]*$)/', $info['file'], $reg)) {
1210 $info['filebody'] = $reg[1];
1211 $info['fileext'] = strtolower($reg[2]);
1212 $info['realFileext'] = $reg[2];
1213 } else {
1214 $info['filebody'] = $info['file'];
1215 $info['fileext'] = '';
1216 }
1217 reset($info);
1218 return $info;
1219 }
1220
1221 /**
1222 * Returns the directory part of a path without trailing slash
1223 * If there is no dir-part, then an empty string is returned.
1224 * Behaviour:
1225 *
1226 * '/dir1/dir2/script.php' => '/dir1/dir2'
1227 * '/dir1/' => '/dir1'
1228 * 'dir1/script.php' => 'dir1'
1229 * 'd/script.php' => 'd'
1230 * '/script.php' => ''
1231 * '' => ''
1232 * Usage: 5
1233 *
1234 * @param string Directory name / path
1235 * @return string Processed input value. See function description.
1236 */
1237 public static function dirname($path) {
1238 $p = self::revExplode('/', $path, 2);
1239 return count($p) == 2 ? $p[0] : '';
1240 }
1241
1242 /**
1243 * Modifies a HTML Hex color by adding/subtracting $R,$G and $B integers
1244 * Usage: 11
1245 *
1246 * @param string A hexadecimal color code, #xxxxxx
1247 * @param integer Offset value 0-255
1248 * @param integer Offset value 0-255
1249 * @param integer Offset value 0-255
1250 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1251 * @see modifyHTMLColorAll()
1252 */
1253 public static function modifyHTMLColor($color, $R, $G, $B) {
1254 // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color
1255 $nR = self::intInRange(hexdec(substr($color, 1, 2)) + $R, 0, 255);
1256 $nG = self::intInRange(hexdec(substr($color, 3, 2)) + $G, 0, 255);
1257 $nB = self::intInRange(hexdec(substr($color, 5, 2)) + $B, 0, 255);
1258 return '#' .
1259 substr('0' . dechex($nR), -2) .
1260 substr('0' . dechex($nG), -2) .
1261 substr('0' . dechex($nB), -2);
1262 }
1263
1264 /**
1265 * Modifies a HTML Hex color by adding/subtracting $all integer from all R/G/B channels
1266 * Usage: 6
1267 *
1268 * @param string A hexadecimal color code, #xxxxxx
1269 * @param integer Offset value 0-255 for all three channels.
1270 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1271 * @see modifyHTMLColor()
1272 */
1273 public static function modifyHTMLColorAll($color, $all) {
1274 return self::modifyHTMLColor($color, $all, $all, $all);
1275 }
1276
1277 /**
1278 * Removes comma (if present) in the end of string
1279 * Usage: 2
1280 *
1281 * @param string String from which the comma in the end (if any) will be removed.
1282 * @return string
1283 * @deprecated since TYPO3 4.5, will be removed in TYPO3 4.7 - Use rtrim() directly
1284 */
1285 public static function rm_endcomma($string) {
1286 self::logDeprecatedFunction();
1287
1288 return rtrim($string, ',');
1289 }
1290
1291 /**
1292 * strtoupper which converts danish (and other characters) characters as well
1293 * Usage: 0
1294 *
1295 * @param string String to process
1296 * @return string
1297 * @deprecated since TYPO3 3.5, will be removed in TYPO3 4.6 - Use t3lib_cs::conv_case() instead or for HTML output, wrap your content in <span class="uppercase">...</span>)
1298 * @ignore
1299 */
1300 public static function danish_strtoupper($string) {
1301 self::logDeprecatedFunction();
1302
1303 $value = strtoupper($string);
1304 return strtr($value, array(
1305 chr(225) => chr(193),
1306 chr(233) => chr(201),
1307 chr(250) => chr(218),
1308 chr(237) => chr(205),
1309 chr(226) => chr(196),
1310 chr(234) => chr(203),
1311 chr(251) => chr(220),
1312 chr(244) => chr(214),
1313 chr(238) => chr(207),
1314 chr(230) => chr(198),
1315 chr(248) => chr(216),
1316 chr(229) => chr(197),
1317 chr(228) => chr(196),
1318 chr(246) => chr(214),
1319 chr(252) => chr(220),
1320 ));
1321 }
1322
1323 /**
1324 * Change umlaut characters to plain ASCII with normally two character target
1325 * Only known characters will be converted, so don't expect a result for any character.
1326 *
1327 * ä => ae, Ö => Oe
1328 *
1329 * @param string String to convert.
1330 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Works only for western europe single-byte charsets! Use t3lib_cs::specCharsToASCII() instead!
1331 * @return string
1332 */
1333 public static function convUmlauts($str) {
1334 self::logDeprecatedFunction();
1335
1336 $pattern = array(chr(228), chr(196), chr(246), chr(214), chr(252), chr(220), chr(223), chr(229), chr(197), chr(248), chr(216), chr(230), chr(198));
1337 $replace = array('ae', 'Ae', 'oe', 'Oe', 'ue', 'Ue', 'ss', 'aa', 'AA', 'oe', 'OE', 'ae', 'AE');
1338 return str_replace($pattern, $replace, $str);
1339 }
1340
1341 /**
1342 * Tests if the input can be interpreted as integer.
1343 *
1344 * @param mixed Any input variable to test
1345 * @return boolean Returns true if string is an integer
1346 */
1347 public static function testInt($var) {
1348 if ($var === '') {
1349 return FALSE;
1350 }
1351 return (string) intval($var) === (string) $var;
1352 }
1353
1354 /**
1355 * Returns true if the first part of $str matches the string $partStr
1356 * Usage: 59
1357 *
1358 * @param string Full string to check
1359 * @param string Reference string which must be found as the "first part" of the full string
1360 * @return boolean True if $partStr was found to be equal to the first part of $str
1361 */
1362 public static function isFirstPartOfStr($str, $partStr) {
1363 return $partStr != '' && strpos((string) $str, (string) $partStr, 0) === 0;
1364 }
1365
1366 /**
1367 * Formats the input integer $sizeInBytes as bytes/kilobytes/megabytes (-/K/M)
1368 * Usage: 53
1369 *
1370 * @param integer Number of bytes to format.
1371 * @param string Labels for bytes, kilo, mega and giga separated by vertical bar (|) and possibly encapsulated in "". Eg: " | K| M| G" (which is the default value)
1372 * @return string Formatted representation of the byte number, for output.
1373 */
1374 public static function formatSize($sizeInBytes, $labels = '') {
1375
1376 // Set labels:
1377 if (strlen($labels) == 0) {
1378 $labels = ' | K| M| G';
1379 } else {
1380 $labels = str_replace('"', '', $labels);
1381 }
1382 $labelArr = explode('|', $labels);
1383
1384 // Find size:
1385 if ($sizeInBytes > 900) {
1386 if ($sizeInBytes > 900000000) { // GB
1387 $val = $sizeInBytes / (1024 * 1024 * 1024);
1388 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[3];
1389 }
1390 elseif ($sizeInBytes > 900000) { // MB
1391 $val = $sizeInBytes / (1024 * 1024);
1392 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[2];
1393 } else { // KB
1394 $val = $sizeInBytes / (1024);
1395 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[1];
1396 }
1397 } else { // Bytes
1398 return $sizeInBytes . $labelArr[0];
1399 }
1400 }
1401
1402 /**
1403 * Returns microtime input to milliseconds
1404 * Usage: 2
1405 *
1406 * @param string Microtime
1407 * @return integer Microtime input string converted to an integer (milliseconds)
1408 */
1409 public static function convertMicrotime($microtime) {
1410 $parts = explode(' ', $microtime);
1411 return round(($parts[0] + $parts[1]) * 1000);
1412 }
1413
1414 /**
1415 * This splits a string by the chars in $operators (typical /+-*) and returns an array with them in
1416 * Usage: 2
1417 *
1418 * @param string Input string, eg "123 + 456 / 789 - 4"
1419 * @param string Operators to split by, typically "/+-*"
1420 * @return array Array with operators and operands separated.
1421 * @see tslib_cObj::calc(), tslib_gifBuilder::calcOffset()
1422 */
1423 public static function splitCalc($string, $operators) {
1424 $res = Array();
1425 $sign = '+';
1426 while ($string) {
1427 $valueLen = strcspn($string, $operators);
1428 $value = substr($string, 0, $valueLen);
1429 $res[] = Array($sign, trim($value));
1430 $sign = substr($string, $valueLen, 1);
1431 $string = substr($string, $valueLen + 1);
1432 }
1433 reset($res);
1434 return $res;
1435 }
1436
1437 /**
1438 * Calculates the input by +,-,*,/,%,^ with priority to + and -
1439 * Usage: 1
1440 *
1441 * @param string Input string, eg "123 + 456 / 789 - 4"
1442 * @return integer Calculated value. Or error string.
1443 * @see calcParenthesis()
1444 */
1445 public static function calcPriority($string) {
1446 $string = preg_replace('/[[:space:]]*/', '', $string); // removing all whitespace
1447 $string = '+' . $string; // Ensuring an operator for the first entrance
1448 $qm = '\*\/\+-^%';
1449 $regex = '([' . $qm . '])([' . $qm . ']?[0-9\.]*)';
1450 // split the expression here:
1451 $reg = array();
1452 preg_match_all('/' . $regex . '/', $string, $reg);
1453
1454 reset($reg[2]);
1455 $number = 0;
1456 $Msign = '+';
1457 $err = '';
1458 $buffer = doubleval(current($reg[2]));
1459 next($reg[2]); // Advance pointer
1460
1461 while (list($k, $v) = each($reg[2])) {
1462 $v = doubleval($v);
1463 $sign = $reg[1][$k];
1464 if ($sign == '+' || $sign == '-') {
1465 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer;
1466 $Msign = $sign;
1467 $buffer = $v;
1468 } else {
1469 if ($sign == '/') {
1470 if ($v) {
1471 $buffer /= $v;
1472 } else {
1473 $err = 'dividing by zero';
1474 }
1475 }
1476 if ($sign == '%') {
1477 if ($v) {
1478 $buffer %= $v;
1479 } else {
1480 $err = 'dividing by zero';
1481 }
1482 }
1483 if ($sign == '*') {
1484 $buffer *= $v;
1485 }
1486 if ($sign == '^') {
1487 $buffer = pow($buffer, $v);
1488 }
1489 }
1490 }
1491 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer;
1492 return $err ? 'ERROR: ' . $err : $number;
1493 }
1494
1495 /**
1496 * Calculates the input with parenthesis levels
1497 * Usage: 2
1498 *
1499 * @param string Input string, eg "(123 + 456) / 789 - 4"
1500 * @return integer Calculated value. Or error string.
1501 * @see calcPriority(), tslib_cObj::stdWrap()
1502 */
1503 public static function calcParenthesis($string) {
1504 $securC = 100;
1505 do {
1506 $valueLenO = strcspn($string, '(');
1507 $valueLenC = strcspn($string, ')');
1508 if ($valueLenC == strlen($string) || $valueLenC < $valueLenO) {
1509 $value = self::calcPriority(substr($string, 0, $valueLenC));
1510 $string = $value . substr($string, $valueLenC + 1);
1511 return $string;
1512 } else {
1513 $string = substr($string, 0, $valueLenO) . self::calcParenthesis(substr($string, $valueLenO + 1));
1514 }
1515 // Security:
1516 $securC--;
1517 if ($securC <= 0) {
1518 break;
1519 }
1520 } while ($valueLenO < strlen($string));
1521 return $string;
1522 }
1523
1524 /**
1525 * Inverse version of htmlspecialchars()
1526 * Usage: 4
1527 *
1528 * @param string Value where &gt;, &lt;, &quot; and &amp; should be converted to regular chars.
1529 * @return string Converted result.
1530 */
1531 public static function htmlspecialchars_decode($value) {
1532 $value = str_replace('&gt;', '>', $value);
1533 $value = str_replace('&lt;', '<', $value);
1534 $value = str_replace('&quot;', '"', $value);
1535 $value = str_replace('&amp;', '&', $value);
1536 return $value;
1537 }
1538
1539 /**
1540 * Re-converts HTML entities if they have been converted by htmlspecialchars()
1541 * Usage: 10
1542 *
1543 * @param string String which contains eg. "&amp;amp;" which should stay "&amp;". Or "&amp;#1234;" to "&#1234;". Or "&amp;#x1b;" to "&#x1b;"
1544 * @return string Converted result.
1545 */
1546 public static function deHSCentities($str) {
1547 return preg_replace('/&amp;([#[:alnum:]]*;)/', '&\1', $str);
1548 }
1549
1550 /**
1551 * This function is used to escape any ' -characters when transferring text to JavaScript!
1552 * Usage: 3
1553 *
1554 * @param string String to escape
1555 * @param boolean If set, also backslashes are escaped.
1556 * @param string The character to escape, default is ' (single-quote)
1557 * @return string Processed input string
1558 */
1559 public static function slashJS($string, $extended = 0, $char = "'") {
1560 if ($extended) {
1561 $string = str_replace("\\", "\\\\", $string);
1562 }
1563 return str_replace($char, "\\" . $char, $string);
1564 }
1565
1566 /**
1567 * Version of rawurlencode() where all spaces (%20) are re-converted to space-characters.
1568 * Usefull when passing text to JavaScript where you simply url-encode it to get around problems with syntax-errors, linebreaks etc.
1569 * Usage: 4
1570 *
1571 * @param string String to raw-url-encode with spaces preserved
1572 * @return string Rawurlencoded result of input string, but with all %20 (space chars) converted to real spaces.
1573 */
1574 public static function rawUrlEncodeJS($str) {
1575 return str_replace('%20', ' ', rawurlencode($str));
1576 }
1577
1578 /**
1579 * rawurlencode which preserves "/" chars
1580 * Usefull when filepaths should keep the "/" chars, but have all other special chars encoded.
1581 * Usage: 5
1582 *
1583 * @param string Input string
1584 * @return string Output string
1585 */
1586 public static function rawUrlEncodeFP($str) {
1587 return str_replace('%2F', '/', rawurlencode($str));
1588 }
1589
1590 /**
1591 * Checking syntax of input email address
1592 * Usage: 5
1593 *
1594 * @param string Input string to evaluate
1595 * @return boolean Returns true if the $email address (input string) is valid
1596 */
1597 public static function validEmail($email) {
1598 // enforce maximum length to prevent libpcre recursion crash bug #52929 in PHP
1599 // fixed in PHP 5.2+ later than Sept 2010; length restriction per SMTP RFC 2821
1600 if (strlen($email) > 320) {
1601 return FALSE;
1602 }
1603 return (filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE);
1604 }
1605
1606 /**
1607 * Checks if current e-mail sending method does not accept recipient/sender name
1608 * in a call to PHP mail() function. Windows version of mail() and mini_sendmail
1609 * program are known not to process such input correctly and they cause SMTP
1610 * errors. This function will return true if current mail sending method has
1611 * problem with recipient name in recipient/sender argument for mail().
1612 *
1613 * TODO: 4.3 should have additional configuration variable, which is combined
1614 * by || with the rest in this function.
1615 *
1616 * @return boolean true if mail() does not accept recipient name
1617 */
1618 public static function isBrokenEmailEnvironment() {
1619 return TYPO3_OS == 'WIN' || (FALSE !== strpos(ini_get('sendmail_path'), 'mini_sendmail'));
1620 }
1621
1622 /**
1623 * Changes from/to arguments for mail() function to work in any environment.
1624 *
1625 * @param string $address Address to adjust
1626 * @return string Adjusted address
1627 * @see t3lib_::isBrokenEmailEnvironment()
1628 */
1629 public static function normalizeMailAddress($address) {
1630 if (self::isBrokenEmailEnvironment() && FALSE !== ($pos1 = strrpos($address, '<'))) {
1631 $pos2 = strpos($address, '>', $pos1);
1632 $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1);
1633 }
1634 return $address;
1635 }
1636
1637 /**
1638 * Formats a string for output between <textarea>-tags
1639 * All content outputted in a textarea form should be passed through this function
1640 * Not only is the content htmlspecialchar'ed on output but there is also a single newline added in the top. The newline is necessary because browsers will ignore the first newline after <textarea> if that is the first character. Therefore better set it!
1641 * Usage: 23
1642 *
1643 * @param string Input string to be formatted.
1644 * @return string Formatted for <textarea>-tags
1645 */
1646 public static function formatForTextarea($content) {
1647 return LF . htmlspecialchars($content);
1648 }
1649
1650 /**
1651 * Converts string to uppercase
1652 * The function converts all Latin characters (a-z, but no accents, etc) to
1653 * uppercase. It is safe for all supported character sets (incl. utf-8).
1654 * Unlike strtoupper() it does not honour the locale.
1655 *
1656 * @param string Input string
1657 * @return string Uppercase String
1658 */
1659 public static function strtoupper($str) {
1660 return strtr((string) $str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
1661 }
1662
1663 /**
1664 * Converts string to lowercase
1665 * The function converts all Latin characters (A-Z, but no accents, etc) to
1666 * lowercase. It is safe for all supported character sets (incl. utf-8).
1667 * Unlike strtolower() it does not honour the locale.
1668 *
1669 * @param string Input string
1670 * @return string Lowercase String
1671 */
1672 public static function strtolower($str) {
1673 return strtr((string) $str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
1674 }
1675
1676 /**
1677 * Returns a string of highly randomized bytes (over the full 8-bit range).
1678 *
1679 * @copyright Drupal CMS
1680 * @license GNU General Public License version 2
1681 * @param integer Number of characters (bytes) to return
1682 * @return string Random Bytes
1683 */
1684 public static function generateRandomBytes($count) {
1685 $output = '';
1686 // /dev/urandom is available on many *nix systems and is considered
1687 // the best commonly available pseudo-random source.
1688 if (TYPO3_OS != 'WIN' && ($fh = @fopen('/dev/urandom', 'rb'))) {
1689 $output = fread($fh, $count);
1690 fclose($fh);
1691 } elseif (TYPO3_OS == 'WIN') {
1692 if (class_exists('COM')) {
1693 try {
1694 $com = new COM('CAPICOM.Utilities.1');
1695 $output = base64_decode($com->GetRandom($count, 0));
1696 } catch (Exception $e) {
1697 // CAPICOM not installed
1698 }
1699 }
1700 if ($output === '' && version_compare(PHP_VERSION, '5.3.0', '>=')) {
1701 if (function_exists('mcrypt_create_iv')) {
1702 $output = mcrypt_create_iv($count, MCRYPT_DEV_URANDOM);
1703 } elseif (function_exists('openssl_random_pseudo_bytes')) {
1704 $isStrong = NULL;
1705 $output = openssl_random_pseudo_bytes($count, $isStrong);
1706 // skip ssl since it wasn't using the strong algo
1707 if ($isStrong !== TRUE) {
1708 $output = '';
1709 }
1710 }
1711 }
1712 }
1713
1714 // fallback if other random byte generation failed until now
1715 if (!isset($output{$count - 1})) {
1716 // We initialize with the somewhat random.
1717 $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
1718 . base_convert(memory_get_usage() % pow(10, 6), 10, 2)
1719 . microtime() . uniqid('') . getmypid();
1720 while (!isset($output{$count - 1})) {
1721 $randomState = sha1(microtime() . mt_rand() . $randomState);
1722 $output .= sha1(mt_rand() . $randomState, TRUE);
1723 }
1724 $output = substr($output, strlen($output) - $count, $count);
1725 }
1726 return $output;
1727 }
1728
1729 /**
1730 * Returns a hex representation of a random byte string.
1731 *
1732 * @param integer Number of hex characters to return
1733 * @return string Random Bytes
1734 */
1735 public static function getRandomHexString($count) {
1736 return substr(bin2hex(self::generateRandomBytes(intval(($count + 1) / 2))), 0, $count);
1737 }
1738
1739 /**
1740 * Returns a given string with underscores as UpperCamelCase.
1741 * Example: Converts blog_example to BlogExample
1742 *
1743 * @param string $string: String to be converted to camel case
1744 * @return string UpperCamelCasedWord
1745 */
1746 public static function underscoredToUpperCamelCase($string) {
1747 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1748 return $upperCamelCase;
1749 }
1750
1751 /**
1752 * Returns a given string with underscores as lowerCamelCase.
1753 * Example: Converts minimal_value to minimalValue
1754 *
1755 * @param string $string: String to be converted to camel case
1756 * @return string lowerCamelCasedWord
1757 */
1758 public static function underscoredToLowerCamelCase($string) {
1759 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1760 $lowerCamelCase = self::lcfirst($upperCamelCase);
1761 return $lowerCamelCase;
1762 }
1763
1764 /**
1765 * Returns a given CamelCasedString as an lowercase string with underscores.
1766 * Example: Converts BlogExample to blog_example, and minimalValue to minimal_value
1767 *
1768 * @param string $string: String to be converted to lowercase underscore
1769 * @return string lowercase_and_underscored_string
1770 */
1771 public static function camelCaseToLowerCaseUnderscored($string) {
1772 return self::strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string));
1773 }
1774
1775 /**
1776 * Converts the first char of a string to lowercase if it is a latin character (A-Z).
1777 * Example: Converts "Hello World" to "hello World"
1778 *
1779 * @param string $string: The string to be used to lowercase the first character
1780 * @return string The string with the first character as lowercase
1781 */
1782 public static function lcfirst($string) {
1783 return self::strtolower(substr($string, 0, 1)) . substr($string, 1);
1784 }
1785
1786 /**
1787 * Checks if a given string is a Uniform Resource Locator (URL).
1788 *
1789 * @param string $url: The URL to be validated
1790 * @return boolean Whether the given URL is valid
1791 */
1792 public static function isValidUrl($url) {
1793 return (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED) !== FALSE);
1794 }
1795
1796
1797 /*************************
1798 *
1799 * ARRAY FUNCTIONS
1800 *
1801 *************************/
1802
1803 /**
1804 * Check if an string item exists in an array.
1805 * Please note that the order of function parameters is reverse compared to the PHP function in_array()!!!
1806 *
1807 * Comparison to PHP in_array():
1808 * -> $array = array(0, 1, 2, 3);
1809 * -> variant_a := t3lib_div::inArray($array, $needle)
1810 * -> variant_b := in_array($needle, $array)
1811 * -> variant_c := in_array($needle, $array, true)
1812 * +---------+-----------+-----------+-----------+
1813 * | $needle | variant_a | variant_b | variant_c |
1814 * +---------+-----------+-----------+-----------+
1815 * | '1a' | false | true | false |
1816 * | '' | false | true | false |
1817 * | '0' | true | true | false |
1818 * | 0 | true | true | true |
1819 * +---------+-----------+-----------+-----------+
1820 * Usage: 3
1821 *
1822 * @param array one-dimensional array of items
1823 * @param string item to check for
1824 * @return boolean true if $item is in the one-dimensional array $in_array
1825 */
1826 public static function inArray(array $in_array, $item) {
1827 foreach ($in_array as $val) {
1828 if (!is_array($val) && !strcmp($val, $item)) {
1829 return TRUE;
1830 }
1831 }
1832 return FALSE;
1833 }
1834
1835 /**
1836 * Explodes a $string delimited by $delim and passes each item in the array through intval().
1837 * Corresponds to t3lib_div::trimExplode(), but with conversion to integers for all values.
1838 * Usage: 76
1839 *
1840 * @param string Delimiter string to explode with
1841 * @param string The string to explode
1842 * @param boolean If set, all empty values (='') will NOT be set in output
1843 * @param integer If positive, the result will contain a maximum of limit elements,
1844 * if negative, all components except the last -limit are returned,
1845 * if zero (default), the result is not limited at all
1846 * @return array Exploded values, all converted to integers
1847 */
1848 public static function intExplode($delimiter, $string, $onlyNonEmptyValues = FALSE, $limit = 0) {
1849 $explodedValues = self::trimExplode($delimiter, $string, $onlyNonEmptyValues, $limit);
1850 return array_map('intval', $explodedValues);
1851 }
1852
1853 /**
1854 * Reverse explode which explodes the string counting from behind.
1855 * Thus t3lib_div::revExplode(':','my:words:here',2) will return array('my:words','here')
1856 * Usage: 8
1857 *
1858 * @param string Delimiter string to explode with
1859 * @param string The string to explode
1860 * @param integer Number of array entries
1861 * @return array Exploded values
1862 */
1863 public static function revExplode($delimiter, $string, $count = 0) {
1864 $explodedValues = explode($delimiter, strrev($string), $count);
1865 $explodedValues = array_map('strrev', $explodedValues);
1866 return array_reverse($explodedValues);
1867 }
1868
1869 /**
1870 * Explodes a string and trims all values for whitespace in the ends.
1871 * If $onlyNonEmptyValues is set, then all blank ('') values are removed.
1872 * Usage: 256
1873 *
1874 * @param string Delimiter string to explode with
1875 * @param string The string to explode
1876 * @param boolean If set, all empty values will be removed in output
1877 * @param integer If positive, the result will contain a maximum of
1878 * $limit elements, if negative, all components except
1879 * the last -$limit are returned, if zero (default),
1880 * the result is not limited at all. Attention though
1881 * that the use of this parameter can slow down this
1882 * function.
1883 * @return array Exploded values
1884 */
1885 public static function trimExplode($delim, $string, $removeEmptyValues = FALSE, $limit = 0) {
1886 $explodedValues = explode($delim, $string);
1887
1888 $result = array_map('trim', $explodedValues);
1889
1890 if ($removeEmptyValues) {
1891 $temp = array();
1892 foreach ($result as $value) {
1893 if ($value !== '') {
1894 $temp[] = $value;
1895 }
1896 }
1897 $result = $temp;
1898 }
1899
1900 if ($limit != 0) {
1901 if ($limit < 0) {
1902 $result = array_slice($result, 0, $limit);
1903 } elseif (count($result) > $limit) {
1904 $lastElements = array_slice($result, $limit - 1);
1905 $result = array_slice($result, 0, $limit - 1);
1906 $result[] = implode($delim, $lastElements);
1907 }
1908 }
1909
1910 return $result;
1911 }
1912
1913 /**
1914 * Remove duplicate values from an array
1915 * Usage: 0
1916 *
1917 * @param array Array of values to make unique
1918 * @return array
1919 * @ignore
1920 * @deprecated since TYPO3 3.5, will be removed in TYPO3 4.6 - Use the PHP function array_unique instead
1921 */
1922 public static function uniqueArray(array $valueArray) {
1923 self::logDeprecatedFunction();
1924
1925 return array_unique($valueArray);
1926 }
1927
1928 /**
1929 * Removes the value $cmpValue from the $array if found there. Returns the modified array
1930 * Usage: 3
1931 *
1932 * @param array Array containing the values
1933 * @param string Value to search for and if found remove array entry where found.
1934 * @return array Output array with entries removed if search string is found
1935 */
1936 public static function removeArrayEntryByValue(array $array, $cmpValue) {
1937 foreach ($array as $k => $v) {
1938 if (is_array($v)) {
1939 $array[$k] = self::removeArrayEntryByValue($v, $cmpValue);
1940 } elseif (!strcmp($v, $cmpValue)) {
1941 unset($array[$k]);
1942 }
1943 }
1944 return $array;
1945 }
1946
1947 /**
1948 * Filters an array to reduce its elements to match the condition.
1949 * The values in $keepItems can be optionally evaluated by a custom callback function.
1950 *
1951 * Example (arguments used to call this function):
1952 * $array = array(
1953 * array('aa' => array('first', 'second'),
1954 * array('bb' => array('third', 'fourth'),
1955 * array('cc' => array('fifth', 'sixth'),
1956 * );
1957 * $keepItems = array('third');
1958 * $getValueFunc = create_function('$value', 'return $value[0];');
1959 *
1960 * Returns:
1961 * array(
1962 * array('bb' => array('third', 'fourth'),
1963 * )
1964 *
1965 * @param array $array: The initial array to be filtered/reduced
1966 * @param mixed $keepItems: The items which are allowed/kept in the array - accepts array or csv string
1967 * @param string $getValueFunc: (optional) Unique function name set by create_function() used to get the value to keep
1968 * @return array The filtered/reduced array with the kept items
1969 */
1970 public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = NULL) {
1971 if ($array) {
1972 // Convert strings to arrays:
1973 if (is_string($keepItems)) {
1974 $keepItems = self::trimExplode(',', $keepItems);
1975 }
1976 // create_function() returns a string:
1977 if (!is_string($getValueFunc)) {
1978 $getValueFunc = NULL;
1979 }
1980 // Do the filtering:
1981 if (is_array($keepItems) && count($keepItems)) {
1982 foreach ($array as $key => $value) {
1983 // Get the value to compare by using the callback function:
1984 $keepValue = (isset($getValueFunc) ? $getValueFunc($value) : $value);
1985 if (!in_array($keepValue, $keepItems)) {
1986 unset($array[$key]);
1987 }
1988 }
1989 }
1990 }
1991 return $array;
1992 }
1993
1994 /**
1995 * Implodes a multidim-array into GET-parameters (eg. &param[key][key2]=value2&param[key][key3]=value3)
1996 * Usage: 24
1997 *
1998 * @param string Name prefix for entries. Set to blank if you wish none.
1999 * @param array The (multidim) array to implode
2000 * @param string (keep blank)
2001 * @param boolean If set, parameters which were blank strings would be removed.
2002 * @param boolean If set, the param name itself (for example "param[key][key2]") would be rawurlencoded as well.
2003 * @return string Imploded result, fx. &param[key][key2]=value2&param[key][key3]=value3
2004 * @see explodeUrl2Array()
2005 */
2006 public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = FALSE, $rawurlencodeParamName = FALSE) {
2007 foreach ($theArray as $Akey => $AVal) {
2008 $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
2009 if (is_array($AVal)) {
2010 $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
2011 } else {
2012 if (!$skipBlank || strcmp($AVal, '')) {
2013 $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) .
2014 '=' . rawurlencode($AVal);
2015 }
2016 }
2017 }
2018 return $str;
2019 }
2020
2021 /**
2022 * Explodes a string with GETvars (eg. "&id=1&type=2&ext[mykey]=3") into an array
2023 *
2024 * @param string GETvars string
2025 * @param boolean If set, the string will be parsed into a multidimensional array if square brackets are used in variable names (using PHP function parse_str())
2026 * @return array Array of values. All values AND keys are rawurldecoded() as they properly should be. But this means that any implosion of the array again must rawurlencode it!
2027 * @see implodeArrayForUrl()
2028 */
2029 public static function explodeUrl2Array($string, $multidim = FALSE) {
2030 $output = array();
2031 if ($multidim) {
2032 parse_str($string, $output);
2033 } else {
2034 $p = explode('&', $string);
2035 foreach ($p as $v) {
2036 if (strlen($v)) {
2037 list($pK, $pV) = explode('=', $v, 2);
2038 $output[rawurldecode($pK)] = rawurldecode($pV);
2039 }
2040 }
2041 }
2042 return $output;
2043 }
2044
2045 /**
2046 * Returns an array with selected keys from incoming data.
2047 * (Better read source code if you want to find out...)
2048 * Usage: 3
2049 *
2050 * @param string List of variable/key names
2051 * @param array Array from where to get values based on the keys in $varList
2052 * @param boolean If set, then t3lib_div::_GP() is used to fetch the value if not found (isset) in the $getArray
2053 * @return array Output array with selected variables.
2054 */
2055 public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = 1) {
2056 $keys = self::trimExplode(',', $varList, 1);
2057 $outArr = array();
2058 foreach ($keys as $v) {
2059 if (isset($getArray[$v])) {
2060 $outArr[$v] = $getArray[$v];
2061 } elseif ($GPvarAlt) {
2062 $outArr[$v] = self::_GP($v);
2063 }
2064 }
2065 return $outArr;
2066 }
2067
2068 /**
2069 * AddSlash array
2070 * This function traverses a multidimentional array and adds slashes to the values.
2071 * NOTE that the input array is and argument by reference.!!
2072 * Twin-function to stripSlashesOnArray
2073 * Usage: 8
2074 *
2075 * @param array Multidimensional input array, (REFERENCE!)
2076 * @return array
2077 */
2078 public static function addSlashesOnArray(array &$theArray) {
2079 foreach ($theArray as &$value) {
2080 if (is_array($value)) {
2081 self::addSlashesOnArray($value);
2082 } else {
2083 $value = addslashes($value);
2084 }
2085 unset($value);
2086 }
2087 reset($theArray);
2088 }
2089
2090 /**
2091 * StripSlash array
2092 * This function traverses a multidimentional array and strips slashes to the values.
2093 * NOTE that the input array is and argument by reference.!!
2094 * Twin-function to addSlashesOnArray
2095 * Usage: 10
2096 *
2097 * @param array Multidimensional input array, (REFERENCE!)
2098 * @return array
2099 */
2100 public static function stripSlashesOnArray(array &$theArray) {
2101 foreach ($theArray as &$value) {
2102 if (is_array($value)) {
2103 self::stripSlashesOnArray($value);
2104 } else {
2105 $value = stripslashes($value);
2106 }
2107 unset($value);
2108 }
2109 reset($theArray);
2110 }
2111
2112 /**
2113 * Either slashes ($cmd=add) or strips ($cmd=strip) array $arr depending on $cmd
2114 * Usage: 0
2115 *
2116 * @param array Multidimensional input array
2117 * @param string "add" or "strip", depending on usage you wish.
2118 * @return array
2119 */
2120 public static function slashArray(array $arr, $cmd) {
2121 if ($cmd == 'strip') {
2122 self::stripSlashesOnArray($arr);
2123 }
2124 if ($cmd == 'add') {
2125 self::addSlashesOnArray($arr);
2126 }
2127 return $arr;
2128 }
2129
2130 /**
2131 * Rename Array keys with a given mapping table
2132 * @param array Array by reference which should be remapped
2133 * @param array Array with remap information, array/$oldKey => $newKey)
2134 */
2135 function remapArrayKeys(&$array, $mappingTable) {
2136 if (is_array($mappingTable)) {
2137 foreach ($mappingTable as $old => $new) {
2138 if ($new && isset($array[$old])) {
2139 $array[$new] = $array[$old];
2140 unset ($array[$old]);
2141 }
2142 }
2143 }
2144 }
2145
2146
2147 /**
2148 * Merges two arrays recursively and "binary safe" (integer keys are
2149 * overridden as well), overruling similar values in the first array
2150 * ($arr0) with the values of the second array ($arr1)
2151 * In case of identical keys, ie. keeping the values of the second.
2152 * Usage: 0
2153 *
2154 * @param array First array
2155 * @param array Second array, overruling the first array
2156 * @param boolean If set, keys that are NOT found in $arr0 (first array) will not be set. Thus only existing value can/will be overruled from second array.
2157 * @param boolean If set, values from $arr1 will overrule if they are empty or zero. Default: true
2158 * @return array Resulting array where $arr1 values has overruled $arr0 values
2159 */
2160 public static function array_merge_recursive_overrule(array $arr0, array $arr1, $notAddKeys = 0, $includeEmtpyValues = TRUE) {
2161 foreach ($arr1 as $key => $val) {
2162 if (is_array($arr0[$key])) {
2163 if (is_array($arr1[$key])) {
2164 $arr0[$key] = self::array_merge_recursive_overrule($arr0[$key], $arr1[$key], $notAddKeys, $includeEmtpyValues);
2165 }
2166 } else {
2167 if ($notAddKeys) {
2168 if (isset($arr0[$key])) {
2169 if ($includeEmtpyValues || $val) {
2170 $arr0[$key] = $val;
2171 }
2172 }
2173 } else {
2174 if ($includeEmtpyValues || $val) {
2175 $arr0[$key] = $val;
2176 }
2177 }
2178 }
2179 }
2180 reset($arr0);
2181 return $arr0;
2182 }
2183
2184 /**
2185 * An array_merge function where the keys are NOT renumbered as they happen to be with the real php-array_merge function. It is "binary safe" in the sense that integer keys are overridden as well.
2186 * Usage: 16
2187 *
2188 * @param array First array
2189 * @param array Second array
2190 * @return array Merged result.
2191 */
2192 public static function array_merge(array $arr1, array $arr2) {
2193 return $arr2 + $arr1;
2194 }
2195
2196 /**
2197 * Filters keys off from first array that also exist in second array. Comparision is done by keys.
2198 * This method is a recursive version of php array_diff_assoc()
2199 *
2200 * @param array Source array
2201 * @param array Reduce source array by this array
2202 * @return array Source array reduced by keys also present in second array
2203 */
2204 public static function arrayDiffAssocRecursive(array $array1, array $array2) {
2205 $differenceArray = array();
2206 foreach ($array1 as $key => $value) {
2207 if (!array_key_exists($key, $array2)) {
2208 $differenceArray[$key] = $value;
2209 } elseif (is_array($value)) {
2210 if (is_array($array2[$key])) {
2211 $differenceArray[$key] = self::arrayDiffAssocRecursive($value, $array2[$key]);
2212 }
2213 }
2214 }
2215
2216 return $differenceArray;
2217 }
2218
2219 /**
2220 * Takes a row and returns a CSV string of the values with $delim (default is ,) and $quote (default is ") as separator chars.
2221 * Usage: 5
2222 *
2223 * @param array Input array of values
2224 * @param string Delimited, default is comman
2225 * @param string Quote-character to wrap around the values.
2226 * @return string A single line of CSV
2227 */
2228 public static function csvValues(array $row, $delim = ',', $quote = '"') {
2229 reset($row);
2230 $out = array();
2231 foreach ($row as $value) {
2232 $out[] = str_replace($quote, $quote . $quote, $value);
2233 }
2234 $str = $quote . implode($quote . $delim . $quote, $out) . $quote;
2235 return $str;
2236 }
2237
2238 /**
2239 * Creates recursively a JSON literal from a multidimensional associative array.
2240 * Uses native function of PHP >= 5.2.0
2241 *
2242 * @param array $jsonArray: The array to be transformed to JSON
2243 * @return string JSON string
2244 * @deprecated since TYPO3 4.3, will be removed in TYPO3 4.6 - use PHP native function json_encode() instead, will be removed in TYPO3 4.5
2245 */
2246 public static function array2json(array $jsonArray) {
2247 self::logDeprecatedFunction();
2248
2249 return json_encode($jsonArray);
2250 }
2251
2252 /**
2253 * Removes dots "." from end of a key identifier of TypoScript styled array.
2254 * array('key.' => array('property.' => 'value')) --> array('key' => array('property' => 'value'))
2255 *
2256 * @param array $ts: TypoScript configuration array
2257 * @return array TypoScript configuration array without dots at the end of all keys
2258 */
2259 public static function removeDotsFromTS(array $ts) {
2260 $out = array();
2261 foreach ($ts as $key => $value) {
2262 if (is_array($value)) {
2263 $key = rtrim($key, '.');
2264 $out[$key] = self::removeDotsFromTS($value);
2265 } else {
2266 $out[$key] = $value;
2267 }
2268 }
2269 return $out;
2270 }
2271
2272 /**
2273 * Sorts an array by key recursive - uses natural sort order (aAbB-zZ)
2274 *
2275 * @param array $array array to be sorted recursively, passed by reference
2276 * @return boolean TRUE if param is an array
2277 */
2278 public static function naturalKeySortRecursive(&$array) {
2279 if (!is_array($array)) {
2280 return FALSE;
2281 }
2282 uksort($array, 'strcasecmp');
2283 foreach ($array as $key => $value) {
2284 self::naturalKeySortRecursive($array[$key]);
2285 }
2286 return TRUE;
2287 }
2288
2289
2290 /*************************
2291 *
2292 * HTML/XML PROCESSING
2293 *
2294 *************************/
2295
2296 /**
2297 * Returns an array with all attributes of the input HTML tag as key/value pairs. Attributes are only lowercase a-z
2298 * $tag is either a whole tag (eg '<TAG OPTION ATTRIB=VALUE>') or the parameterlist (ex ' OPTION ATTRIB=VALUE>')
2299 * If an attribute is empty, then the value for the key is empty. You can check if it existed with isset()
2300 * Usage: 8
2301 *
2302 * @param string HTML-tag string (or attributes only)
2303 * @return array Array with the attribute values.
2304 */
2305 public static function get_tag_attributes($tag) {
2306 $components = self::split_tag_attributes($tag);
2307 $name = ''; // attribute name is stored here
2308 $valuemode = FALSE;
2309 $attributes = array();
2310 foreach ($components as $key => $val) {
2311 if ($val != '=') { // Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
2312 if ($valuemode) {
2313 if ($name) {
2314 $attributes[$name] = $val;
2315 $name = '';
2316 }
2317 } else {
2318 if ($key = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $val))) {
2319 $attributes[$key] = '';
2320 $name = $key;
2321 }
2322 }
2323 $valuemode = FALSE;
2324 } else {
2325 $valuemode = TRUE;
2326 }
2327 }
2328 return $attributes;
2329 }
2330
2331 /**
2332 * Returns an array with the 'components' from an attribute list from an HTML tag. The result is normally analyzed by get_tag_attributes
2333 * Removes tag-name if found
2334 * Usage: 2
2335 *
2336 * @param string HTML-tag string (or attributes only)
2337 * @return array Array with the attribute values.
2338 */
2339 public static function split_tag_attributes($tag) {
2340 $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
2341 // Removes any > in the end of the string
2342 $tag_tmp = trim(rtrim($tag_tmp, '>'));
2343
2344 $value = array();
2345 while (strcmp($tag_tmp, '')) { // Compared with empty string instead , 030102
2346 $firstChar = substr($tag_tmp, 0, 1);
2347 if (!strcmp($firstChar, '"') || !strcmp($firstChar, "'")) {
2348 $reg = explode($firstChar, $tag_tmp, 3);
2349 $value[] = $reg[1];
2350 $tag_tmp = trim($reg[2]);
2351 } elseif (!strcmp($firstChar, '=')) {
2352 $value[] = '=';
2353 $tag_tmp = trim(substr($tag_tmp, 1)); // Removes = chars.
2354 } else {
2355 // There are '' around the value. We look for the next ' ' or '>'
2356 $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
2357 $value[] = trim($reg[0]);
2358 $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
2359 }
2360 }
2361 reset($value);
2362 return $value;
2363 }
2364
2365 /**
2366 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
2367 * Usage: 14
2368 *
2369 * @param array Array with attribute key/value pairs, eg. "bgcolor"=>"red", "border"=>0
2370 * @param boolean If set the resulting attribute list will have a) all attributes in lowercase (and duplicates weeded out, first entry taking precedence) and b) all values htmlspecialchar()'ed. It is recommended to use this switch!
2371 * @param boolean If true, don't check if values are blank. Default is to omit attributes with blank values.
2372 * @return string Imploded attributes, eg. 'bgcolor="red" border="0"'
2373 */
2374 public static function implodeAttributes(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) {
2375 if ($xhtmlSafe) {
2376 $newArr = array();
2377 foreach ($arr as $p => $v) {
2378 if (!isset($newArr[strtolower($p)])) {
2379 $newArr[strtolower($p)] = htmlspecialchars($v);
2380 }
2381 }
2382 $arr = $newArr;
2383 }
2384 $list = array();
2385 foreach ($arr as $p => $v) {
2386 if (strcmp($v, '') || $dontOmitBlankAttribs) {
2387 $list[] = $p . '="' . $v . '"';
2388 }
2389 }
2390 return implode(' ', $list);
2391 }
2392
2393 /**
2394 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
2395 *
2396 * @param array See implodeAttributes()
2397 * @param boolean See implodeAttributes()
2398 * @param boolean See implodeAttributes()
2399 * @return string See implodeAttributes()
2400 * @deprecated since TYPO3 3.7, will be removed in TYPO3 4.6 - Name was changed into implodeAttributes
2401 * @see implodeAttributes()
2402 */
2403 public static function implodeParams(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) {
2404 self::logDeprecatedFunction();
2405
2406 return self::implodeAttributes($arr, $xhtmlSafe, $dontOmitBlankAttribs);
2407 }
2408
2409 /**
2410 * Wraps JavaScript code XHTML ready with <script>-tags
2411 * Automatic re-identing of the JS code is done by using the first line as ident reference.
2412 * This is nice for identing JS code with PHP code on the same level.
2413 *
2414 * @param string JavaScript code
2415 * @param boolean Wrap script element in linebreaks? Default is TRUE.
2416 * @return string The wrapped JS code, ready to put into a XHTML page
2417 * @author Ingmar Schlecht <ingmars@web.de>
2418 * @author René Fritz <r.fritz@colorcube.de>
2419 */
2420 public static function wrapJS($string, $linebreak = TRUE) {
2421 if (trim($string)) {
2422 // <script wrapped in nl?
2423 $cr = $linebreak ? LF : '';
2424
2425 // remove nl from the beginning
2426 $string = preg_replace('/^\n+/', '', $string);
2427 // re-ident to one tab using the first line as reference
2428 $match = array();
2429 if (preg_match('/^(\t+)/', $string, $match)) {
2430 $string = str_replace($match[1], TAB, $string);
2431 }
2432 $string = $cr . '<script type="text/javascript">
2433 /*<![CDATA[*/
2434 ' . $string . '
2435 /*]]>*/
2436 </script>' . $cr;
2437 }
2438 return trim($string);
2439 }
2440
2441
2442 /**
2443 * Parses XML input into a PHP array with associative keys
2444 * Usage: 0
2445 *
2446 * @param string XML data input
2447 * @param integer Number of element levels to resolve the XML into an array. Any further structure will be set as XML.
2448 * @return mixed The array with the parsed structure unless the XML parser returns with an error in which case the error message string is returned.
2449 * @author bisqwit at iki dot fi dot not dot for dot ads dot invalid / http://dk.php.net/xml_parse_into_struct + kasperYYYY@typo3.com
2450 */
2451 public static function xml2tree($string, $depth = 999) {
2452 $parser = xml_parser_create();
2453 $vals = array();
2454 $index = array();
2455
2456 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2457 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2458 xml_parse_into_struct($parser, $string, $vals, $index);
2459
2460 if (xml_get_error_code($parser)) {
2461 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2462 }
2463 xml_parser_free($parser);
2464
2465 $stack = array(array());
2466 $stacktop = 0;
2467 $startPoint = 0;
2468
2469 $tagi = array();
2470 foreach ($vals as $key => $val) {
2471 $type = $val['type'];
2472
2473 // open tag:
2474 if ($type == 'open' || $type == 'complete') {
2475 $stack[$stacktop++] = $tagi;
2476
2477 if ($depth == $stacktop) {
2478 $startPoint = $key;
2479 }
2480
2481 $tagi = array('tag' => $val['tag']);
2482
2483 if (isset($val['attributes'])) {
2484 $tagi['attrs'] = $val['attributes'];
2485 }
2486 if (isset($val['value'])) {
2487 $tagi['values'][] = $val['value'];
2488 }
2489 }
2490 // finish tag:
2491 if ($type == 'complete' || $type == 'close') {
2492 $oldtagi = $tagi;
2493 $tagi = $stack[--$stacktop];
2494 $oldtag = $oldtagi['tag'];
2495 unset($oldtagi['tag']);
2496
2497 if ($depth == ($stacktop + 1)) {
2498 if ($key - $startPoint > 0) {
2499 $partArray = array_slice(
2500 $vals,
2501 $startPoint + 1,
2502 $key - $startPoint - 1
2503 );
2504 #$oldtagi=array('XMLvalue'=>self::xmlRecompileFromStructValArray($partArray));
2505 $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray);
2506 } else {
2507 $oldtagi['XMLvalue'] = $oldtagi['values'][0];
2508 }
2509 }
2510
2511 $tagi['ch'][$oldtag][] = $oldtagi;
2512 unset($oldtagi);
2513 }
2514 // cdata
2515 if ($type == 'cdata') {
2516 $tagi['values'][] = $val['value'];
2517 }
2518 }
2519 return $tagi['ch'];
2520 }
2521
2522 /**
2523 * Turns PHP array into XML. See array2xml()
2524 *
2525 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2526 * @param string Alternative document tag. Default is "phparray".
2527 * @param array Options for the compilation. See array2xml() for description.
2528 * @param string Forced charset to prologue
2529 * @return string An XML string made from the input content in the array.
2530 * @see xml2array(),array2xml()
2531 */
2532 public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = array(), $charset = '') {
2533
2534 // Figure out charset if not given explicitly:
2535 if (!$charset) {
2536 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { // First priority: forceCharset! If set, this will be authoritative!
2537 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
2538 } elseif (is_object($GLOBALS['LANG'])) {
2539 $charset = $GLOBALS['LANG']->charSet; // If "LANG" is around, that will hold the current charset
2540 } else {
2541 $charset = 'iso-8859-1'; // THIS is just a hopeful guess!
2542 }
2543 }
2544
2545 // Return XML:
2546 return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF .
2547 self::array2xml($array, '', 0, $docTag, 0, $options);
2548 }
2549
2550 /**
2551 * Deprecated to call directly (unless you are aware of using XML prologues)! Use "array2xml_cs" instead (which adds an XML-prologue)
2552 *
2553 * Converts a PHP array into an XML string.
2554 * The XML output is optimized for readability since associative keys are used as tagnames.
2555 * This also means that only alphanumeric characters are allowed in the tag names AND only keys NOT starting with numbers (so watch your usage of keys!). However there are options you can set to avoid this problem.
2556 * Numeric keys are stored with the default tagname "numIndex" but can be overridden to other formats)
2557 * The function handles input values from the PHP array in a binary-safe way; All characters below 32 (except 9,10,13) will trigger the content to be converted to a base64-string
2558 * The PHP variable type of the data IS preserved as long as the types are strings, arrays, integers and booleans. Strings are the default type unless the "type" attribute is set.
2559 * The output XML has been tested with the PHP XML-parser and parses OK under all tested circumstances with 4.x versions. However, with PHP5 there seems to be the need to add an XML prologue a la <?xml version="1.0" encoding="[charset]" standalone="yes" ?> - otherwise UTF-8 is assumed! Unfortunately, many times the output from this function is used without adding that prologue meaning that non-ASCII characters will break the parsing!! This suchs of course! Effectively it means that the prologue should always be prepended setting the right characterset, alternatively the system should always run as utf-8!
2560 * However using MSIE to read the XML output didn't always go well: One reason could be that the character encoding is not observed in the PHP data. The other reason may be if the tag-names are invalid in the eyes of MSIE. Also using the namespace feature will make MSIE break parsing. There might be more reasons...
2561 * Usage: 5
2562 *
2563 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2564 * @param string tag-prefix, eg. a namespace prefix like "T3:"
2565 * @param integer Current recursion level. Don't change, stay at zero!
2566 * @param string Alternative document tag. Default is "phparray".
2567 * @param integer If greater than zero, then the number of spaces corresponding to this number is used for indenting, if less than zero - no indentation, if zero - a single TAB is used
2568 * @param array Options for the compilation. Key "useNindex" => 0/1 (boolean: whether to use "n0, n1, n2" for num. indexes); Key "useIndexTagForNum" => "[tag for numerical indexes]"; Key "useIndexTagForAssoc" => "[tag for associative indexes"; Key "parentTagMap" => array('parentTag' => 'thisLevelTag')
2569 * @param string Stack data. Don't touch.
2570 * @return string An XML string made from the input content in the array.
2571 * @see xml2array()
2572 */
2573 public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = array(), array $stackData = array()) {
2574 // The list of byte values which will trigger binary-safe storage. If any value has one of these char values in it, it will be encoded in base64
2575 $binaryChars = chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) .
2576 chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) .
2577 chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) .
2578 chr(30) . chr(31);
2579 // Set indenting mode:
2580 $indentChar = $spaceInd ? ' ' : TAB;
2581 $indentN = $spaceInd > 0 ? $spaceInd : 1;
2582 $nl = ($spaceInd >= 0 ? LF : '');
2583
2584 // Init output variable:
2585 $output = '';
2586
2587 // Traverse the input array
2588 foreach ($array as $k => $v) {
2589 $attr = '';
2590 $tagName = $k;
2591
2592 // Construct the tag name.
2593 if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) { // Use tag based on grand-parent + parent tag name
2594 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2595 $tagName = (string) $options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']];
2596 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && self::testInt($tagName)) { // Use tag based on parent tag name + if current tag is numeric
2597 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2598 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM'];
2599 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) { // Use tag based on parent tag name + current tag
2600 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2601 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName];
2602 } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) { // Use tag based on parent tag name:
2603 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2604 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName']];
2605 } elseif (!strcmp(intval($tagName), $tagName)) { // If integer...;
2606 if ($options['useNindex']) { // If numeric key, prefix "n"
2607 $tagName = 'n' . $tagName;
2608 } else { // Use special tag for num. keys:
2609 $attr .= ' index="' . $tagName . '"';
2610 $tagName = $options['useIndexTagForNum'] ? $options['useIndexTagForNum'] : 'numIndex';
2611 }
2612 } elseif ($options['useIndexTagForAssoc']) { // Use tag for all associative keys:
2613 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2614 $tagName = $options['useIndexTagForAssoc'];
2615 }
2616
2617 // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
2618 $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
2619
2620 // If the value is an array then we will call this function recursively:
2621 if (is_array($v)) {
2622
2623 // Sub elements:
2624 if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) {
2625 $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName];
2626 $clearStackPath = $subOptions['clearStackPath'];
2627 } else {
2628 $subOptions = $options;
2629 $clearStackPath = FALSE;
2630 }
2631
2632 $content = $nl .
2633 self::array2xml(
2634 $v,
2635 $NSprefix,
2636 $level + 1,
2637 '',
2638 $spaceInd,
2639 $subOptions,
2640 array(
2641 'parentTagName' => $tagName,
2642 'grandParentTagName' => $stackData['parentTagName'],
2643 'path' => $clearStackPath ? '' : $stackData['path'] . '/' . $tagName,
2644 )
2645 ) .
2646 ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
2647 if ((int) $options['disableTypeAttrib'] != 2) { // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
2648 $attr .= ' type="array"';
2649 }
2650 } else { // Just a value:
2651
2652 // Look for binary chars:
2653 $vLen = strlen($v); // check for length, because PHP 5.2.0 may crash when first argument of strcspn is empty
2654 if ($vLen && strcspn($v, $binaryChars) != $vLen) { // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
2655 // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
2656 $content = $nl . chunk_split(base64_encode($v));
2657 $attr .= ' base64="1"';
2658 } else {
2659 // Otherwise, just htmlspecialchar the stuff:
2660 $content = htmlspecialchars($v);
2661 $dType = gettype($v);
2662 if ($dType == 'string') {
2663 if ($options['useCDATA'] && $content != $v) {
2664 $content = '<![CDATA[' . $v . ']]>';
2665 }
2666 } elseif (!$options['disableTypeAttrib']) {
2667 $attr .= ' type="' . $dType . '"';
2668 }
2669 }
2670 }
2671
2672 // Add the element to the output string:
2673 $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '') . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl;
2674 }
2675
2676 // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
2677 if (!$level) {
2678 $output =
2679 '<' . $docTag . '>' . $nl .
2680 $output .
2681 '</' . $docTag . '>';
2682 }
2683
2684 return $output;
2685 }
2686
2687 /**
2688 * Converts an XML string to a PHP array.
2689 * This is the reverse function of array2xml()
2690 * This is a wrapper for xml2arrayProcess that adds a two-level cache
2691 * Usage: 17
2692 *
2693 * @param string XML content to convert into an array
2694 * @param string The tag-prefix resolve, eg. a namespace like "T3:"
2695 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array
2696 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content.
2697 * @see array2xml(),xml2arrayProcess()
2698 * @author Fabrizio Branca <typo3@fabrizio-branca.de> (added caching)
2699 */
2700 public static function xml2array($string, $NSprefix = '', $reportDocTag = FALSE) {
2701 static $firstLevelCache = array();
2702
2703 $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
2704
2705 // look up in first level cache
2706 if (!empty($firstLevelCache[$identifier])) {
2707 $array = $firstLevelCache[$identifier];
2708 } else {
2709 // look up in second level cache
2710 $cacheContent = t3lib_pageSelect::getHash($identifier, 0);
2711 $array = unserialize($cacheContent);
2712
2713 if ($array === FALSE) {
2714 $array = self::xml2arrayProcess($string, $NSprefix, $reportDocTag);
2715 t3lib_pageSelect::storeHash($identifier, serialize($array), 'ident_xml2array');
2716 }
2717 // store content in first level cache
2718 $firstLevelCache[$identifier] = $array;
2719 }
2720 return $array;
2721 }
2722
2723 /**
2724 * Converts an XML string to a PHP array.
2725 * This is the reverse function of array2xml()
2726 * Usage: 1
2727 *
2728 * @param string XML content to convert into an array
2729 * @param string The tag-prefix resolve, eg. a namespace like "T3:"
2730 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array
2731 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content.
2732 * @see array2xml()
2733 */
2734 protected function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = FALSE) {
2735 global $TYPO3_CONF_VARS;
2736
2737 // Create parser:
2738 $parser = xml_parser_create();
2739 $vals = array();
2740 $index = array();
2741
2742 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2743 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2744
2745 // default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2746 $match = array();
2747 preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
2748 $theCharset = $match[1] ? $match[1] : ($TYPO3_CONF_VARS['BE']['forceCharset'] ? $TYPO3_CONF_VARS['BE']['forceCharset'] : 'iso-8859-1');
2749 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1
2750
2751 // Parse content:
2752 xml_parse_into_struct($parser, $string, $vals, $index);
2753
2754 // If error, return error message:
2755 if (xml_get_error_code($parser)) {
2756 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2757 }
2758 xml_parser_free($parser);
2759
2760 // Init vars:
2761 $stack = array(array());
2762 $stacktop = 0;
2763 $current = array();
2764 $tagName = '';
2765 $documentTag = '';
2766
2767 // Traverse the parsed XML structure:
2768 foreach ($vals as $key => $val) {
2769
2770 // First, process the tag-name (which is used in both cases, whether "complete" or "close")
2771 $tagName = $val['tag'];
2772 if (!$documentTag) {
2773 $documentTag = $tagName;
2774 }
2775
2776 // Test for name space:
2777 $tagName = ($NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix) ? substr($tagName, strlen($NSprefix)) : $tagName;
2778
2779 // Test for numeric tag, encoded on the form "nXXX":
2780 $testNtag = substr($tagName, 1); // Closing tag.
2781 $tagName = (substr($tagName, 0, 1) == 'n' && !strcmp(intval($testNtag), $testNtag)) ? intval($testNtag) : $tagName;
2782
2783 // Test for alternative index value:
2784 if (strlen($val['attributes']['index'])) {
2785 $tagName = $val['attributes']['index'];
2786 }
2787
2788 // Setting tag-values, manage stack:
2789 switch ($val['type']) {
2790 case 'open': // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
2791 $current[$tagName] = array(); // Setting blank place holder
2792 $stack[$stacktop++] = $current;
2793 $current = array();
2794 break;
2795 case 'close': // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
2796 $oldCurrent = $current;
2797 $current = $stack[--$stacktop];
2798 end($current); // Going to the end of array to get placeholder key, key($current), and fill in array next:
2799 $current[key($current)] = $oldCurrent;
2800 unset($oldCurrent);
2801 break;
2802 case 'complete': // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
2803 if ($val['attributes']['base64']) {
2804 $current[$tagName] = base64_decode($val['value']);
2805 } else {
2806 $current[$tagName] = (string) $val['value']; // Had to cast it as a string - otherwise it would be evaluate false if tested with isset()!!
2807
2808 // Cast type:
2809 switch ((string) $val['attributes']['type']) {
2810 case 'integer':
2811 $current[$tagName] = (integer) $current[$tagName];
2812 break;
2813 case 'double':
2814 $current[$tagName] = (double) $current[$tagName];
2815 break;
2816 case 'boolean':
2817 $current[$tagName] = (bool) $current[$tagName];
2818 break;
2819 case 'array':
2820 $current[$tagName] = array(); // MUST be an empty array since it is processed as a value; Empty arrays would end up here because they would have no tags inside...
2821 break;
2822 }
2823 }
2824 break;
2825 }
2826 }
2827
2828 if ($reportDocTag) {
2829 $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
2830 }
2831
2832 // Finally return the content of the document tag.
2833 return $current[$tagName];
2834 }
2835
2836 /**
2837 * This implodes an array of XML parts (made with xml_parse_into_struct()) into XML again.
2838 * Usage: 2
2839 *
2840 * @param array A array of XML parts, see xml2tree
2841 * @return string Re-compiled XML data.
2842 */
2843 public static function xmlRecompileFromStructValArray(array $vals) {
2844 $XMLcontent = '';
2845
2846 foreach ($vals as $val) {
2847 $type = $val['type'];
2848
2849 // open tag:
2850 if ($type == 'open' || $type == 'complete') {
2851 $XMLcontent .= '<' . $val['tag'];
2852 if (isset($val['attributes'])) {
2853 foreach ($val['attributes'] as $k => $v) {
2854 $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
2855 }
2856 }
2857 if ($type == 'complete') {
2858 if (isset($val['value'])) {
2859 $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
2860 } else {
2861 $XMLcontent .= '/>';
2862 }
2863 } else {
2864 $XMLcontent .= '>';
2865 }
2866
2867 if ($type == 'open' && isset($val['value'])) {
2868 $XMLcontent .= htmlspecialchars($val['value']);
2869 }
2870 }
2871 // finish tag:
2872 if ($type == 'close') {
2873 $XMLcontent .= '</' . $val['tag'] . '>';
2874 }
2875 // cdata
2876 if ($type == 'cdata') {
2877 $XMLcontent .= htmlspecialchars($val['value']);
2878 }
2879 }
2880
2881 return $XMLcontent;
2882 }
2883
2884 /**
2885 * Extracts the attributes (typically encoding and version) of an XML prologue (header).
2886 * Usage: 1
2887 *
2888 * @param string XML data
2889 * @return array Attributes of the xml prologue (header)
2890 */
2891 public static function xmlGetHeaderAttribs($xmlData) {
2892 $match = array();
2893 if (preg_match('/^\s*<\?xml([^>]*)\?\>/', $xmlData, $match)) {
2894 return self::get_tag_attributes($match[1]);
2895 }
2896 }
2897
2898 /**
2899 * Minifies JavaScript
2900 *
2901 * @param string $script Script to minify
2902 * @param string $error Error message (if any)
2903 * @return string Minified script or source string if error happened
2904 */
2905 public static function minifyJavaScript($script, &$error = '') {
2906 require_once(PATH_typo3 . 'contrib/jsmin/jsmin.php');
2907 try {
2908 $error = '';
2909 $script = trim(JSMin::minify(str_replace(CR, '', $script)));
2910 }
2911 catch (JSMinException $e) {
2912 $error = 'Error while minifying JavaScript: ' . $e->getMessage();
2913 self::devLog($error, 't3lib_div', 2,
2914 array('JavaScript' => $script, 'Stack trace' => $e->getTrace()));
2915 }
2916 return $script;
2917 }
2918
2919
2920 /*************************
2921 *
2922 * FILES FUNCTIONS
2923 *
2924 *************************/
2925
2926 /**
2927 * Reads the file or url $url and returns the content
2928 * If you are having trouble with proxys when reading URLs you can configure your way out of that with settings like $TYPO3_CONF_VARS['SYS']['curlUse'] etc.
2929 * Usage: 83
2930 *
2931 * @param string File/URL to read
2932 * @param integer Whether the HTTP header should be fetched or not. 0=disable, 1=fetch header+content, 2=fetch header only
2933 * @param array HTTP headers to be used in the request
2934 * @param array Error code/message and, if $includeHeader is 1, response meta data (HTTP status and content type)
2935 * @return string The content from the resource given as input. FALSE if an error has occured.
2936 */
2937 public static function getURL($url, $includeHeader = 0, $requestHeaders = FALSE, &$report = NULL) {
2938 $content = FALSE;
2939
2940 if (isset($report)) {
2941 $report['error'] = 0;
2942 $report['message'] = '';
2943 }
2944
2945 // use cURL for: http, https, ftp, ftps, sftp and scp
2946 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
2947 if (isset($report)) {
2948 $report['lib'] = 'cURL';
2949 }
2950
2951 // External URL without error checking.
2952 $ch = curl_init();
2953 if (!$ch) {
2954 if (isset($report)) {
2955 $report['error'] = -1;
2956 $report['message'] = 'Couldn\'t initialize cURL.';
2957 }
2958 return FALSE;
2959 }
2960
2961 curl_setopt($ch, CURLOPT_URL, $url);
2962 curl_setopt($ch, CURLOPT_HEADER, $includeHeader ? 1 : 0);
2963 curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0);
2964 curl_setopt($ch, CURLOPT_HTTPGET, $includeHeader == 2 ? 'HEAD' : 'GET');
2965 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2966 curl_setopt($ch, CURLOPT_FAILONERROR, 1);
2967 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, max(0, intval($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlTimeout'])));
2968
2969 // may fail (PHP 5.2.0+ and 5.1.5+) when open_basedir or safe_mode are enabled
2970 $followLocation = @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
2971
2972 if (is_array($requestHeaders)) {
2973 curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2974 }
2975
2976 // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>)
2977 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
2978 curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
2979
2980 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
2981 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
2982 }
2983 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
2984 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
2985 }
2986 }
2987 $content = curl_exec($ch);
2988 if (isset($report)) {
2989 if ($content === FALSE) {
2990 $report['error'] = curl_errno($ch);
2991 $report['message'] = curl_error($ch);
2992 } else {
2993 $curlInfo = curl_getinfo($ch);
2994 // We hit a redirection but we couldn't follow it
2995 if (!$followLocation && $curlInfo['status'] >= 300 && $curlInfo['status'] < 400) {
2996 $report['error'] = -1;
2997 $report['message'] = 'Couldn\'t follow location redirect (either PHP configuration option safe_mode or open_basedir is in effect).';
2998 } elseif ($includeHeader) {
2999 // Set only for $includeHeader to work exactly like PHP variant
3000 $report['http_code'] = $curlInfo['http_code'];
3001 $report['content_type'] = $curlInfo['content_type'];
3002 }
3003 }
3004 }
3005 curl_close($ch);
3006
3007 } elseif ($includeHeader) {
3008 if (isset($report)) {
3009 $report['lib'] = 'socket';
3010 }
3011 $parsedURL = parse_url($url);
3012 if (!preg_match('/^https?/', $parsedURL['scheme'])) {
3013 if (isset($report)) {
3014 $report['error'] = -1;
3015 $report['message'] = 'Reading headers is not allowed for this protocol.';
3016 }
3017 return FALSE;
3018 }
3019 $port = intval($parsedURL['port']);
3020 if ($port < 1) {
3021 if ($parsedURL['scheme'] == 'http') {
3022 $port = ($port > 0 ? $port : 80);
3023 $scheme = '';
3024 } else {
3025 $port = ($port > 0 ? $port : 443);
3026 $scheme = 'ssl://';
3027 }
3028 }
3029 $errno = 0;
3030 // $errstr = '';
3031 $fp = @fsockopen($scheme . $parsedURL['host'], $port, $errno, $errstr, 2.0);
3032 if (!$fp || $errno > 0) {
3033 if (isset($report)) {
3034 $report['error'] = $errno ? $errno : -1;
3035 $report['message'] = $errno ? ($errstr ? $errstr : 'Socket error.') : 'Socket initialization error.';
3036 }
3037 return FALSE;
3038 }
3039 $method = ($includeHeader == 2) ? 'HEAD' : 'GET';
3040 $msg = $method . ' ' . $parsedURL['path'] .
3041 ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') .
3042 ' HTTP/1.0' . CRLF . 'Host: ' .
3043 $parsedURL['host'] . "\r\nConnection: close\r\n";
3044 if (is_array($requestHeaders)) {
3045 $msg .= implode(CRLF, $requestHeaders) . CRLF;
3046 }
3047 $msg .= CRLF;
3048
3049 fputs($fp, $msg);
3050 while (!feof($fp)) {
3051 $line = fgets($fp, 2048);
3052 if (isset($report)) {
3053 if (preg_match('|^HTTP/\d\.\d +(\d+)|', $line, $status)) {
3054 $report['http_code'] = $status[1];
3055 }
3056 elseif (preg_match('/^Content-Type: *(.*)/i', $line, $type)) {
3057 $report['content_type'] = $type[1];
3058 }
3059 }
3060 $content .= $line;
3061 if (!strlen(trim($line))) {
3062 break; // Stop at the first empty line (= end of header)
3063 }
3064 }
3065 if ($includeHeader != 2) {
3066 $content .= stream_get_contents($fp);
3067 }
3068 fclose($fp);
3069
3070 } elseif (is_array($requestHeaders)) {
3071 if (isset($report)) {
3072 $report['lib'] = 'file/context';
3073 }
3074 $parsedURL = parse_url($url);
3075 if (!preg_match('/^https?/', $parsedURL['scheme'])) {
3076 if (isset($report)) {
3077 $report['error'] = -1;
3078 $report['message'] = 'Sending request headers is not allowed for this protocol.';
3079 }
3080 return FALSE;
3081 }
3082 $ctx = stream_context_create(array(
3083 'http' => array(
3084 'header' => implode(CRLF, $requestHeaders)
3085 )
3086 )
3087 );
3088 $content = @file_get_contents($url, FALSE, $ctx);
3089 if ($content === FALSE && isset($report)) {
3090 $phpError = error_get_last();
3091 $report['error'] = $phpError['type'];
3092 $report['message'] = $phpError['message'];
3093 }
3094 } else {
3095 if (isset($report)) {
3096 $report['lib'] = 'file';
3097 }
3098 $content = @file_get_contents($url);
3099 if ($content === FALSE && isset($report)) {
3100 if (function_exists('error_get_last')) {
3101 $phpError = error_get_last();
3102 $report['error'] = $phpError['type'];
3103 $report['message'] = $phpError['message'];
3104 } else {
3105 $report['error'] = -1;
3106 $report['message'] = 'Couldn\'t get URL.';
3107 }
3108 }
3109 }
3110
3111 return $content;
3112 }
3113
3114 /**
3115 * Writes $content to the file $file
3116 * Usage: 30
3117 *
3118 * @param string Filepath to write to
3119 * @param string Content to write
3120 * @return boolean True if the file was successfully opened and written to.
3121 */
3122 public static function writeFile($file, $content) {
3123 if (!@is_file($file)) {
3124 $changePermissions = TRUE;
3125 }
3126
3127 if ($fd = fopen($file, 'wb')) {
3128 $res = fwrite($fd, $content);
3129 fclose($fd);
3130
3131 if ($res === FALSE) {
3132 return FALSE;
3133 }
3134
3135 if ($changePermissions) { // Change the permissions only if the file has just been created
3136 self::fixPermissions($file);
3137 }
3138
3139 return TRUE;
3140 }
3141
3142 return FALSE;
3143 }
3144
3145 /**
3146 * Sets the file system mode and group ownership of a file or a folder.
3147 *
3148 * @param string Path of file or folder, must not be escaped. Path can be absolute or relative
3149 * @param boolean If set, also fixes permissions of files and folders in the folder (if $path is a folder)
3150 * @return mixed TRUE on success, FALSE on error, always TRUE on Windows OS
3151 */
3152 public static function fixPermissions($path, $recursive = FALSE) {
3153 if (TYPO3_OS != 'WIN') {
3154 $result = FALSE;
3155
3156 // Make path absolute
3157 if (!self::isAbsPath($path)) {
3158 $path = self::getFileAbsFileName($path, FALSE);
3159 }
3160
3161 if (self::isAllowedAbsPath($path)) {
3162 if (@is_file($path)) {
3163 // "@" is there because file is not necessarily OWNED by the user
3164 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['fileCreateMask']));
3165 } elseif (@is_dir($path)) {
3166 $path = preg_replace('|/$|', '', $path);
3167 // "@" is there because file is not necessarily OWNED by the user
3168 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']));
3169 }
3170
3171 // Set createGroup if not empty
3172 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']) {
3173 // "@" is there because file is not necessarily OWNED by the user
3174 $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']);
3175 $result = $changeGroupResult ? $result : FALSE;
3176 }
3177
3178 // Call recursive if recursive flag if set and $path is directory
3179 if ($recursive && @is_dir($path)) {
3180 $handle = opendir($path);
3181 while (($file = readdir($handle)) !== FALSE) {
3182 $recursionResult = NULL;
3183 if ($file !== '.' && $file !== '..') {
3184 if (@is_file($path . '/' . $file)) {
3185 $recursionResult = self::fixPermissions($path . '/' . $file);
3186 } elseif (@is_dir($path . '/' . $file)) {
3187 $recursionResult = self::fixPermissions($path . '/' . $file, TRUE);
3188 }
3189 if (isset($recursionResult) && !$recursionResult) {
3190 $result = FALSE;
3191 }
3192 }
3193 }
3194 closedir($handle);
3195 }
3196 }
3197 } else {
3198 $result = TRUE;
3199 }
3200 return $result;
3201 }
3202
3203 /**
3204 * Writes $content to a filename in the typo3temp/ folder (and possibly one or two subfolders...)
3205 * Accepts an additional subdirectory in the file path!
3206 *
3207 * @param string Absolute filepath to write to inside "typo3temp/". First part of this string must match PATH_site."typo3temp/"
3208 * @param string Content string to write
3209 * @return string Returns NULL on success, otherwise an error string telling about the problem.
3210 */
3211 public static function writeFileToTypo3tempDir($filepath, $content) {
3212
3213 // Parse filepath into directory and basename:
3214 $fI = pathinfo($filepath);
3215 $fI['dirname'] .= '/';
3216
3217 // Check parts:
3218 if (self::validPathStr($filepath) && $fI['basename'] && strlen($fI['basename']) < 60) {
3219 if (defined('PATH_site')) {
3220 $dirName = PATH_site . 'typo3temp/'; // Setting main temporary directory name (standard)
3221 if (@is_dir($dirName)) {
3222 if (self::isFirstPartOfStr($fI['dirname'], $dirName)) {
3223
3224 // Checking if the "subdir" is found:
3225 $subdir = substr($fI['dirname'], strlen($dirName));
3226 if ($subdir) {
3227 if (preg_match('/^[[:alnum:]_]+\/$/', $subdir) || preg_match('/^[[:alnum:]_]+\/[[:alnum:]_]+\/$/', $subdir)) {
3228 $dirName .= $subdir;
3229 if (!@is_dir($dirName)) {
3230 self::mkdir_deep(PATH_site . 'typo3temp/', $subdir);
3231 }
3232 } else {
3233 return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"';
3234 }
3235 }
3236 // Checking dir-name again (sub-dir might have been created):
3237 if (@is_dir($dirName)) {
3238 if ($filepath == $dirName . $fI['basename']) {
3239 self::writeFile($filepath, $content);
3240 if (!@is_file($filepath)) {
3241 return 'File not written to disk! Write permission error in filesystem?';
3242 }
3243 } else {
3244 return 'Calculated filelocation didn\'t match input $filepath!';
3245 }
3246 } else {
3247 return '"' . $dirName . '" is not a directory!';
3248 }
3249 } else {
3250 return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"';
3251 }
3252 } else {
3253 return 'PATH_site + "typo3temp/" was not a directory!';
3254 }
3255 } else {
3256 return 'PATH_site constant was NOT defined!';
3257 }
3258 } else {
3259 return 'Input filepath "' . $filepath . '" was generally invalid!';
3260 }
3261 }
3262
3263 /**
3264 * Wrapper function for mkdir.
3265 * Sets folder permissions according to $GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']
3266 * and group ownership according to $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']
3267 *
3268 * @param string Absolute path to folder, see PHP mkdir() function. Removes trailing slash internally.
3269 * @return boolean TRUE if @mkdir went well!
3270 */
3271 public static function mkdir($newFolder) {
3272 $newFolder = preg_replace('|/$|', '', $newFolder);