Cleanup
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_div.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2007 Kasper Skaarhoj (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 Skaarhoj
32 * XHTML compliant
33 * Usage counts are based on search 22/2 2003 through whole source including tslib/
34 *
35 * @author Kasper Skaarhoj <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="\n",$charWidth=76)
62 * 574: function breakLinesForEmail($str,$implChar="\n",$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
206
207
208
209
210
211
212
213
214
215
216 /**
217 * The legendary "t3lib_div" class - Miscellaneous functions for general purpose.
218 * Most of the functions does not relate specifically to TYPO3
219 * However a section of functions requires certain TYPO3 features available
220 * See comments in the source.
221 * You are encouraged to use this library in your own scripts!
222 *
223 * USE:
224 * The class is intended to be used without creating an instance of it.
225 * So: Don't instantiate - call functions with "t3lib_div::" prefixed the function name.
226 * So use t3lib_div::[method-name] to refer to the functions, eg. 't3lib_div::milliseconds()'
227 *
228 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
229 * @package TYPO3
230 * @subpackage t3lib
231 */
232 class t3lib_div {
233
234
235
236
237
238 /*************************
239 *
240 * GET/POST Variables
241 *
242 * Background:
243 * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
244 * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
245 * But the clean solution is that quotes are never escaped and that is what the functions below offers.
246 * Eventually TYPO3 should provide this in the global space as well.
247 * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
248 *
249 *************************/
250
251 /**
252 * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order)
253 * Strips slashes from all output, both strings and arrays.
254 * This function substitutes t3lib_div::GPvar()
255 * 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!
256 * Usage: 537
257 *
258 * @param string GET/POST var to return
259 * @return mixed POST var named $var and if not set, the GET var of the same name.
260 * @see GPvar()
261 */
262 function _GP($var) {
263 if(empty($var)) return;
264 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
265 if (isset($value)) {
266 if (is_array($value)) { t3lib_div::stripSlashesOnArray($value); } else { $value = stripslashes($value); }
267 }
268 return $value;
269 }
270
271 /**
272 * Returns the global GET array (or value from) normalized to contain un-escaped values.
273 * ALWAYS use this API function to acquire the GET variables!
274 * Usage: 27
275 *
276 * @param string Optional pointer to value in GET array (basically name of GET var)
277 * @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!*
278 * @see _POST(), _GP(), _GETset()
279 */
280 function _GET($var=NULL) {
281 $value = ($var === NULL) ? $_GET : (empty($var) ? NULL : $_GET[$var]);
282 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
283 if (is_array($value)) { t3lib_div::stripSlashesOnArray($value); } else { $value = stripslashes($value); }
284 }
285 return $value;
286 }
287
288 /**
289 * Returns the global POST array (or value from) normalized to contain un-escaped values.
290 * ALWAYS use this API function to acquire the POST variables!
291 * Usage: 41
292 *
293 * @param string Optional pointer to value in POST array (basically name of POST var)
294 * @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!*
295 * @see _GET(), _GP()
296 */
297 function _POST($var=NULL) {
298 $value = ($var === NULL) ? $_POST : (empty($var) ? NULL : $_POST[$var]);
299 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
300 if (is_array($value)) { t3lib_div::stripSlashesOnArray($value); } else { $value = stripslashes($value); }
301 }
302 return $value;
303 }
304
305 /**
306 * Writes input value to $_GET
307 * Usage: 2
308 *
309 * @param array Array to write to $_GET. Values should NOT be escaped at input time (but will be escaped before writing according to TYPO3 standards).
310 * @param string Alternative key; If set, this will not set the WHOLE GET array, but only the key in it specified by this value!
311 * @return void
312 */
313 function _GETset($inputGet,$key='') {
314 // ADDS slashes since TYPO3 standard currently is that slashes MUST be applied (regardless of magic_quotes setting).
315 if (strcmp($key,'')) {
316 if (is_array($inputGet)) { t3lib_div::addSlashesOnArray($inputGet); } else { $inputGet = addslashes($inputGet); }
317 $GLOBALS['HTTP_GET_VARS'][$key] = $_GET[$key] = $inputGet;
318 } elseif (is_array($inputGet)){
319 t3lib_div::addSlashesOnArray($inputGet);
320 $GLOBALS['HTTP_GET_VARS'] = $_GET = $inputGet;
321 }
322 }
323
324 /**
325 * GET/POST variable
326 * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order)
327 * Strips slashes of string-outputs, but not arrays UNLESS $strip is set. If $strip is set all output will have escaped characters unescaped.
328 * Usage: 2
329 *
330 * @param string GET/POST var to return
331 * @param boolean If set, values are stripped of return values that are *arrays!* - string/integer values returned are always strip-slashed()
332 * @return mixed POST var named $var and if not set, the GET var of the same name.
333 * @deprecated Use t3lib_div::_GP instead (ALWAYS delivers a value with un-escaped values!)
334 * @see _GP()
335 */
336 function GPvar($var,$strip=0) {
337 if(empty($var)) return;
338 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
339 if (isset($value) && is_string($value)) { $value = stripslashes($value); } // 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.
340 if ($strip && isset($value) && is_array($value)) { t3lib_div::stripSlashesOnArray($value); }
341 return $value;
342 }
343
344 /**
345 * Returns the GET/POST global arrays merged with POST taking precedence.
346 * Usage: 1
347 *
348 * @param string Key (variable name) from GET or POST vars
349 * @return array Returns the GET vars merged recursively onto the POST vars.
350 * @ignore
351 * @deprecated
352 */
353 function GParrayMerged($var) {
354 $postA = is_array($_POST[$var]) ? $_POST[$var] : array();
355 $getA = is_array($_GET[$var]) ? $_GET[$var] : array();
356 $mergedA = t3lib_div::array_merge_recursive_overrule($getA,$postA);
357 t3lib_div::stripSlashesOnArray($mergedA);
358 return $mergedA;
359 }
360
361 /**
362 * Wrapper for the RemoveXSS function.
363 * Removes potential XSS code from an input string.
364 *
365 * Using an external class by Travis Puderbaugh <kallahar@quickwired.com>
366 *
367 * @param string Input string
368 * @return string Input string with potential XSS code removed
369 */
370 public function removeXSS($string) {
371 require_once(PATH_typo3.'contrib/RemoveXSS/RemoveXSS.php');
372 $string = RemoveXSS::RemoveXSS($string);
373 return $string;
374 }
375
376
377
378
379
380
381
382
383
384
385 /*************************
386 *
387 * IMAGE FUNCTIONS
388 *
389 *************************/
390
391
392 /**
393 * Compressing a GIF file if not already LZW compressed
394 * 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)
395 *
396 * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file
397 * GIF:
398 * 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!)
399 * If $type is set to either 'IM' or 'GD' the compression is done with ImageMagick and GD respectively
400 * PNG:
401 * No changes.
402 *
403 * $theFile is expected to be a valid GIF-file!
404 * The function returns a code for the operation.
405 * Usage: 9
406 *
407 * @param string Filepath
408 * @param string See description of function
409 * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string.
410 * @internal
411 */
412 function gif_compress($theFile, $type) {
413 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
414 $returnCode='';
415 if ($gfxConf['gif_compress'] && strtolower(substr($theFile,-4,4))=='.gif') { // GIF...
416 if (($type=='IM' || !$type) && $gfxConf['im'] && $gfxConf['im_path_lzw']) { // IM
417 $cmd = t3lib_div::imageMagickCommand('convert', '"'.$theFile.'" "'.$theFile.'"', $gfxConf['im_path_lzw']);
418 exec($cmd);
419
420 $returnCode='IM';
421 } elseif (($type=='GD' || !$type) && $gfxConf['gdlib'] && !$gfxConf['gdlib_png']) { // GD
422 $tempImage = imageCreateFromGif($theFile);
423 imageGif($tempImage, $theFile);
424 imageDestroy($tempImage);
425 $returnCode='GD';
426 }
427 }
428 return $returnCode;
429 }
430
431 /**
432 * Converts a png file to gif
433 * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set true.
434 * Usage: 5
435 *
436 * @param string $theFile the filename with path
437 * @return string new filename
438 * @internal
439 */
440 function png_to_gif_by_imagemagick($theFile) {
441 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif']
442 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im']
443 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']
444 && strtolower(substr($theFile,-4,4))=='.png'
445 && @is_file($theFile)) { // IM
446 $newFile = substr($theFile,0,-4).'.gif';
447 $cmd = t3lib_div::imageMagickCommand('convert', '"'.$theFile.'" "'.$newFile.'"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']);
448 exec($cmd);
449 $theFile = $newFile;
450 // 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!!
451 }
452 return $theFile;
453 }
454
455 /**
456 * Returns filename of the png/gif version of the input file (which can be png or gif).
457 * If input file type does not match the wanted output type a conversion is made and temp-filename returned.
458 * Usage: 2
459 *
460 * @param string Filepath of image file
461 * @param boolean If set, then input file is converted to PNG, otherwise to GIF
462 * @return string If the new image file exists, it's filepath is returned
463 * @internal
464 */
465 function read_png_gif($theFile,$output_png=0) {
466 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] && @is_file($theFile)) {
467 $ext = strtolower(substr($theFile,-4,4));
468 if (
469 ((string)$ext=='.png' && $output_png) ||
470 ((string)$ext=='.gif' && !$output_png)
471 ) {
472 return $theFile;
473 } else {
474 $newFile = PATH_site.'typo3temp/readPG_'.md5($theFile.'|'.filemtime($theFile)).($output_png?'.png':'.gif');
475 exec($GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path'].'convert "'.$theFile.'" "'.$newFile.'"');
476 if (@is_file($newFile)) return $newFile;
477 }
478 }
479 }
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 /*************************
496 *
497 * STRING FUNCTIONS
498 *
499 *************************/
500
501 /**
502 * Truncate string
503 * Returns a new string of max. $chars length.
504 * If the string is longer, it will be truncated and appended with '...'.
505 * Usage: 39
506 *
507 * @param string string to truncate
508 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
509 * @param string String to append to the output if it is truncated, default is '...'
510 * @return string new string
511 * @deprecated Works ONLY for single-byte charsets! USE t3lib_div::fixed_lgd_cs() instead
512 * @see fixed_lgd_pre()
513 */
514 function fixed_lgd($string,$origChars,$preStr='...') {
515 $chars = abs($origChars);
516 if ($chars >= 4) {
517 if(strlen($string)>$chars) {
518 return $origChars < 0 ?
519 $preStr.trim(substr($string, -($chars-3))) :
520 trim(substr($string, 0, $chars-3)).$preStr;
521 }
522 }
523 return $string;
524 }
525
526 /**
527 * Truncate string
528 * Returns a new string of max. $chars length.
529 * If the string is longer, it will be truncated and prepended with '...'.
530 * This works like fixed_lgd, but is truncated in the start of the string instead of the end
531 * Usage: 6
532 *
533 * @param string string to truncate
534 * @param integer must be an integer of at least 4
535 * @return string new string
536 * @deprecated Use either fixed_lgd() or fixed_lgd_cs() (with negative input value for $chars)
537 * @see fixed_lgd()
538 */
539 function fixed_lgd_pre($string,$chars) {
540 return strrev(t3lib_div::fixed_lgd(strrev($string),$chars));
541 }
542
543 /**
544 * Truncates a string with appended/prepended "..." and takes backend character set into consideration
545 * Use only from backend!
546 * Usage: 75
547 *
548 * @param string string to truncate
549 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
550 * @return string New string
551 * @see fixed_lgd()
552 */
553 function fixed_lgd_cs($string,$chars) {
554 if (is_object($GLOBALS['LANG'])) {
555 return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$string,$chars,'...');
556 } else {
557 return t3lib_div::fixed_lgd($string, $chars);
558 }
559 }
560
561 /**
562 * Breaks up the text for emails
563 * Usage: 1
564 *
565 * @param string The string to break up
566 * @param string The string to implode the broken lines with (default/typically \n)
567 * @param integer The line length
568 * @deprecated Use PHP function wordwrap()
569 * @return string
570 */
571 function breakTextForEmail($str,$implChar="\n",$charWidth=76) {
572 $lines = explode(chr(10),$str);
573 $outArr=array();
574 while(list(,$lStr)=each($lines)) {
575 $outArr[] = t3lib_div::breakLinesForEmail($lStr,$implChar,$charWidth);
576 }
577 return implode(chr(10),$outArr);
578 }
579
580 /**
581 * Breaks up a single line of text for emails
582 * Usage: 5
583 *
584 * @param string The string to break up
585 * @param string The string to implode the broken lines with (default/typically \n)
586 * @param integer The line length
587 * @return string
588 * @see breakTextForEmail()
589 */
590 function breakLinesForEmail($str,$implChar="\n",$charWidth=76) {
591 $lines=array();
592 $l=$charWidth;
593 $p=0;
594 while(strlen($str)>$p) {
595 $substr=substr($str,$p,$l);
596 if (strlen($substr)==$l) {
597 $count = count(explode(' ',trim(strrev($substr))));
598 if ($count>1) { // OK...
599 $parts = explode(' ',strrev($substr),2);
600 $theLine = strrev($parts[1]);
601 } else {
602 $afterParts = explode(' ',substr($str,$l+$p),2);
603 $theLine = $substr.$afterParts[0];
604 }
605 if (!strlen($theLine)) {break; } // Error, because this would keep us in an endless loop.
606 } else {
607 $theLine=$substr;
608 }
609
610 $lines[]=trim($theLine);
611 $p+=strlen($theLine);
612 if (!trim(substr($str,$p,$l))) break; // added...
613 }
614 return implode($implChar,$lines);
615 }
616
617 /**
618 * Match IP number with list of numbers with wildcard
619 * Dispatcher method for switching into specialised IPv4 and IPv6 methods.
620 * Usage: 10
621 *
622 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
623 * @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.
624 * @return boolean True if an IP-mask from $list matches $baseIP
625 */
626 function cmpIP($baseIP, $list) {
627 if ($list==='*') return TRUE;
628 if (strstr($baseIP, ':') && t3lib_div::validIPv6($baseIP)) {
629 return t3lib_div::cmpIPv6($baseIP, $list);
630 } else {
631 return t3lib_div::cmpIPv4($baseIP, $list);
632 }
633 }
634
635 /**
636 * Match IPv4 number with list of numbers with wildcard
637 *
638 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
639 * @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)
640 * @return boolean True if an IP-mask from $list matches $baseIP
641 */
642 function cmpIPv4($baseIP, $list) {
643 $IPpartsReq = explode('.',$baseIP);
644 if (count($IPpartsReq)==4) {
645 $values = t3lib_div::trimExplode(',',$list,1);
646
647 foreach($values as $test) {
648 list($test,$mask) = explode('/',$test);
649
650 if(intval($mask)) {
651 // "192.168.3.0/24"
652 $lnet = ip2long($test);
653 $lip = ip2long($baseIP);
654 $binnet = str_pad( decbin($lnet),32,'0','STR_PAD_LEFT');
655 $firstpart = substr($binnet,0,$mask);
656 $binip = str_pad( decbin($lip),32,'0','STR_PAD_LEFT');
657 $firstip = substr($binip,0,$mask);
658 $yes = (strcmp($firstpart,$firstip)==0);
659 } else {
660 // "192.168.*.*"
661 $IPparts = explode('.',$test);
662 $yes = 1;
663 reset($IPparts);
664 while(list($index,$val)=each($IPparts)) {
665 $val = trim($val);
666 if (strcmp($val,'*') && strcmp($IPpartsReq[$index],$val)) {
667 $yes=0;
668 }
669 }
670 }
671 if ($yes) return true;
672 }
673 }
674 return false;
675 }
676
677 /**
678 * Match IPv6 address with a list of IPv6 prefixes
679 *
680 * @param string $baseIP is the current remote IP address for instance
681 * @param string $list is a comma-list of IPv6 prefixes, could also contain IPv4 addresses
682 * @return boolean True if an baseIP matches any prefix
683 */
684 function cmpIPv6($baseIP, $list) {
685 $success = false; // Policy default: Deny connection
686 $baseIP = t3lib_div::normalizeIPv6($baseIP);
687
688 $values = t3lib_div::trimExplode(',',$list,1);
689 foreach ($values as $test) {
690 list($test,$mask) = explode('/',$test);
691 if (t3lib_div::validIPv6($test)) {
692 $test = t3lib_div::normalizeIPv6($test);
693 if (intval($mask)) {
694 switch ($mask) { // test on /48 /64
695 case '48':
696 $testBin = substr(t3lib_div::IPv6Hex2Bin($test), 0, 48);
697 $baseIPBin = substr(t3lib_div::IPv6Hex2Bin($baseIP), 0, 48);
698 $success = strcmp($testBin, $baseIPBin)==0 ? true : false;
699 break;
700 case '64':
701 $testBin = substr(t3lib_div::IPv6Hex2Bin($test), 0, 64);
702 $baseIPBin = substr(t3lib_div::IPv6Hex2Bin($baseIP), 0, 64);
703 $success = strcmp($testBin, $baseIPBin)==0 ? true : false;
704 break;
705 default:
706 $success = false;
707 }
708 } else {
709 if (t3lib_div::validIPv6($test)) { // test on full ip address 128 bits
710 $testBin = t3lib_div::IPv6Hex2Bin($test);
711 $baseIPBin = t3lib_div::IPv6Hex2Bin($baseIP);
712 $success = strcmp($testBin, $baseIPBin)==0 ? true : false;
713 }
714 }
715 }
716 if ($success) return true;
717 }
718 return false;
719 }
720
721 /**
722 * [Describe function...]
723 *
724 * @param [type] $hex: ...
725 * @return [type] ...
726 */
727 function IPv6Hex2Bin ($hex) {
728 $bin = '';
729 $hex = str_replace(':', '', $hex); // Replace colon to nothing
730 for ($i=0; $i<strlen($hex); $i=$i+2) {
731 $bin.= chr(hexdec(substr($hex, $i, 2)));
732 }
733 return $bin;
734 }
735
736 /**
737 * Normalize an IPv6 address to full length
738 *
739 * @param string Given IPv6 address
740 * @return string Normalized address
741 */
742 function normalizeIPv6($address) {
743 $normalizedAddress = '';
744 $stageOneAddress = '';
745
746 $chunks = explode('::', $address); // Count 2 if if address has hidden zero blocks
747 if (count($chunks)==2) {
748 $chunksLeft = explode(':', $chunks[0]);
749 $chunksRight = explode(':', $chunks[1]);
750 $left = count($chunksLeft);
751 $right = count($chunksRight);
752
753 // Special case: leading zero-only blocks count to 1, should be 0
754 if ($left==1 && strlen($chunksLeft[0])==0) $left=0;
755
756 $hiddenBlocks = 8 - ($left + $right);
757 $hiddenPart = '';
758 while ($h<$hiddenBlocks) {
759 $hiddenPart .= '0000:';
760 $h++;
761 }
762
763 if ($left == 0) {
764 $stageOneAddress = $hiddenPart . $chunks[1];
765 } else {
766 $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
767 }
768 } else $stageOneAddress = $address;
769
770 // normalize the blocks:
771 $blocks = explode(':', $stageOneAddress);
772 $divCounter = 0;
773 foreach ($blocks as $block) {
774 $tmpBlock = '';
775 $i = 0;
776 $hiddenZeros = 4 - strlen($block);
777 while ($i < $hiddenZeros) {
778 $tmpBlock .= '0';
779 $i++;
780 }
781 $normalizedAddress .= $tmpBlock . $block;
782 if ($divCounter < 7) {
783 $normalizedAddress .= ':';
784 $divCounter++;
785 }
786 }
787 return $normalizedAddress;
788 }
789
790 /**
791 * Validate a given IP address to the IPv6 address format.
792 *
793 * Example for possible format: 43FB::BB3F:A0A0:0 | ::1
794 *
795 * @param string IP address to be tested
796 * @return boolean True if $ip is of IPv6 format.
797 */
798 function validIPv6($ip) {
799 $uppercaseIP = strtoupper($ip);
800
801 $regex = '/^(';
802 $regex.= '(([\dA-F]{1,4}:){7}[\dA-F]{1,4})|';
803 $regex.= '(([\dA-F]{1,4}){1}::([\dA-F]{1,4}:){1,5}[\dA-F]{1,4})|';
804 $regex.= '(([\dA-F]{1,4}:){2}:([\dA-F]{1,4}:){1,4}[\dA-F]{1,4})|';
805 $regex.= '(([\dA-F]{1,4}:){3}:([\dA-F]{1,4}:){1,3}[\dA-F]{1,4})|';
806 $regex.= '(([\dA-F]{1,4}:){4}:([\dA-F]{1,4}:){1,2}[\dA-F]{1,4})|';
807 $regex.= '(([\dA-F]{1,4}:){5}:([\dA-F]{1,4}:){0,1}[\dA-F]{1,4})|';
808 $regex.= '(::([\dA-F]{1,4}:){0,6}[\dA-F]{1,4})';
809 $regex.= ')$/';
810
811 return preg_match($regex, $uppercaseIP) ? true : false;
812 }
813
814 /**
815 * Match fully qualified domain name with list of strings with wildcard
816 *
817 * @param string The current remote IP address for instance, typ. REMOTE_ADDR
818 * @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)
819 * @return boolean True if a domain name mask from $list matches $baseIP
820 */
821 function cmpFQDN($baseIP, $list) {
822 if (count(explode('.',$baseIP))==4) {
823 $resolvedHostName = explode('.', gethostbyaddr($baseIP));
824 $values = t3lib_div::trimExplode(',',$list,1);
825
826 foreach($values as $test) {
827 $hostNameParts = explode('.',$test);
828 $yes = 1;
829
830 foreach($hostNameParts as $index => $val) {
831 $val = trim($val);
832 if (strcmp($val,'*') && strcmp($resolvedHostName[$index],$val)) {
833 $yes=0;
834 }
835 }
836 if ($yes) return true;
837 }
838 }
839 return false;
840 }
841
842 /**
843 * Check for item in list
844 * Check if an item exists in a comma-separated list of items.
845 * Usage: 163
846 *
847 * @param string comma-separated list of items (string)
848 * @param string item to check for
849 * @return boolean true if $item is in $list
850 */
851 function inList($list,$item) {
852 return strstr(','.$list.',', ','.$item.',') ? true : false;
853 }
854
855 /**
856 * Removes an item from a comma-separated list of items.
857 * Usage: 1
858 *
859 * @param string element to remove
860 * @param string comma-separated list of items (string)
861 * @return string new comma-separated list of items
862 */
863 function rmFromList($element,$list) {
864 $items = explode(',',$list);
865 while(list($k,$v)=each($items)) {
866 if ($v==$element) {unset($items[$k]);}
867 }
868 return implode(',',$items);
869 }
870
871 /**
872 * Expand a comma-separated list of integers with ranges (eg 1,3-5,7 becomes 1,3,4,5,7).
873 * Ranges are limited to 1000 values per range.
874 *
875 * @param string comma-separated list of integers with ranges (string)
876 * @return string new comma-separated list of items
877 * @author Martin Kutschker <martin.kutschker@activesolution.at>
878 */
879 function expandList($list) {
880 $items = explode(',',$list);
881 $list = array();
882 while(list(,$item)=each($items)) {
883 $range = explode('-',$item);
884 if (isset($range[1])) {
885 $runAwayBrake = 1000;
886 for ($n=$range[0]; $n<=$range[1]; $n++) {
887 $list[] = $n;
888
889 $runAwayBrake--;
890 if ($runAwayBrake<=0) break;
891 }
892 } else {
893 $list[] = $item;
894 }
895 }
896
897 return implode(',',$list);
898 }
899
900 /**
901 * Forces the integer $theInt into the boundaries of $min and $max. If the $theInt is 'false' then the $zeroValue is applied.
902 * Usage: 224
903 *
904 * @param integer Input value
905 * @param integer Lower limit
906 * @param integer Higher limit
907 * @param integer Default value if input is false.
908 * @return integer The input value forced into the boundaries of $min and $max
909 */
910 function intInRange($theInt,$min,$max=2000000000,$zeroValue=0) {
911 // Returns $theInt as an integer in the integerspace from $min to $max
912 $theInt = intval($theInt);
913 if ($zeroValue && !$theInt) {$theInt=$zeroValue;} // If the input value is zero after being converted to integer, zeroValue may set another default value for it.
914 if ($theInt<$min){$theInt=$min;}
915 if ($theInt>$max){$theInt=$max;}
916 return $theInt;
917 }
918
919 /**
920 * Returns the $integer if greater than zero, otherwise returns zero.
921 * Usage: 1
922 *
923 * @param integer Integer string to process
924 * @return integer
925 */
926 function intval_positive($theInt) {
927 $theInt = intval($theInt);
928 if ($theInt<0){$theInt=0;}
929 return $theInt;
930 }
931
932 /**
933 * Returns an integer from a three part version number, eg '4.12.3' -> 4012003
934 * Usage: 2
935 *
936 * @param string Version number on format x.x.x
937 * @return integer Integer version of version number (where each part can count to 999)
938 */
939 function int_from_ver($verNumberStr) {
940 $verParts = explode('.',$verNumberStr);
941 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));
942 }
943
944 /**
945 * Returns true if the current TYPO3 version (or compatibility version) is compatible to the input version
946 * Notice that this function compares branches, not versions (4.0.1 would be > 4.0.0 although they use the same compat_version)
947 *
948 * @param string Minimum branch number required (format x.y / e.g. "4.0" NOT "4.0.0"!)
949 * @return boolean Returns true if this setup is compatible with the provided version number
950 * @todo Still needs a function to convert versions to branches
951 */
952 function compat_version($verNumberStr) {
953 global $TYPO3_CONF_VARS;
954 $currVersionStr = $TYPO3_CONF_VARS['SYS']['compat_version'] ? $TYPO3_CONF_VARS['SYS']['compat_version'] : TYPO3_branch;
955
956 if (t3lib_div::int_from_ver($currVersionStr) < t3lib_div::int_from_ver($verNumberStr)) {
957 return FALSE;
958 } else {
959 return TRUE;
960 }
961 }
962
963 /**
964 * Makes a positive integer hash out of the first 7 chars from the md5 hash of the input
965 * Usage: 5
966 *
967 * @param string String to md5-hash
968 * @return integer Returns 28bit integer-hash
969 */
970 function md5int($str) {
971 return hexdec(substr(md5($str),0,7));
972 }
973
974 /**
975 * Returns the first 10 positions of the MD5-hash (changed from 6 to 10 recently)
976 *
977 * Usage: 37
978 *
979 * @param string Input string to be md5-hashed
980 * @param integer The string-length of the output
981 * @return string Substring of the resulting md5-hash, being $len chars long (from beginning)
982 */
983 function shortMD5($input, $len=10) {
984 return substr(md5($input),0,$len);
985 }
986
987 /**
988 * Takes comma-separated lists and arrays and removes all duplicates
989 * If a value in the list is trim(empty), the value is ignored.
990 * Usage: 16
991 *
992 * @param string Accept multiple parameters wich can be comma-separated lists of values and arrays.
993 * @param mixed $secondParameter: Dummy field, which if set will show a warning!
994 * @return string Returns the list without any duplicates of values, space around values are trimmed
995 */
996 function uniqueList($in_list, $secondParameter=NULL) {
997 if (is_array($in_list)) die('t3lib_div::uniqueList() does NOT support array arguments anymore! Only string comma lists!');
998 if (isset($secondParameter)) die('t3lib_div::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one.');
999
1000 return implode(',',array_unique(t3lib_div::trimExplode(',',$in_list,1)));
1001 }
1002
1003 /**
1004 * Splits a reference to a file in 5 parts
1005 * Usage: 43
1006 *
1007 * @param string Filename/filepath to be analysed
1008 * @return array Contains keys [path], [file], [filebody], [fileext], [realFileext]
1009 */
1010 function split_fileref($fileref) {
1011 $reg = array();
1012 if ( ereg('(.*/)(.*)$',$fileref,$reg) ) {
1013 $info['path'] = $reg[1];
1014 $info['file'] = $reg[2];
1015 } else {
1016 $info['path'] = '';
1017 $info['file'] = $fileref;
1018 }
1019 $reg='';
1020 if ( ereg('(.*)\.([^\.]*$)',$info['file'],$reg) ) {
1021 $info['filebody'] = $reg[1];
1022 $info['fileext'] = strtolower($reg[2]);
1023 $info['realFileext'] = $reg[2];
1024 } else {
1025 $info['filebody'] = $info['file'];
1026 $info['fileext'] = '';
1027 }
1028 reset($info);
1029 return $info;
1030 }
1031
1032 /**
1033 * Returns the directory part of a path without trailing slash
1034 * If there is no dir-part, then an empty string is returned.
1035 * Behaviour:
1036 *
1037 * '/dir1/dir2/script.php' => '/dir1/dir2'
1038 * '/dir1/' => '/dir1'
1039 * 'dir1/script.php' => 'dir1'
1040 * 'd/script.php' => 'd'
1041 * '/script.php' => ''
1042 * '' => ''
1043 * Usage: 5
1044 *
1045 * @param string Directory name / path
1046 * @return string Processed input value. See function description.
1047 */
1048 function dirname($path) {
1049 $p=t3lib_div::revExplode('/',$path,2);
1050 return count($p)==2?$p[0]:'';
1051 }
1052
1053 /**
1054 * Modifies a HTML Hex color by adding/subtracting $R,$G and $B integers
1055 * Usage: 11
1056 *
1057 * @param string A hexadecimal color code, #xxxxxx
1058 * @param integer Offset value 0-255
1059 * @param integer Offset value 0-255
1060 * @param integer Offset value 0-255
1061 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1062 * @see modifyHTMLColorAll()
1063 */
1064 function modifyHTMLColor($color,$R,$G,$B) {
1065 // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color
1066 $nR = t3lib_div::intInRange(hexdec(substr($color,1,2))+$R,0,255);
1067 $nG = t3lib_div::intInRange(hexdec(substr($color,3,2))+$G,0,255);
1068 $nB = t3lib_div::intInRange(hexdec(substr($color,5,2))+$B,0,255);
1069 return '#'.
1070 substr('0'.dechex($nR),-2).
1071 substr('0'.dechex($nG),-2).
1072 substr('0'.dechex($nB),-2);
1073 }
1074
1075 /**
1076 * Modifies a HTML Hex color by adding/subtracting $all integer from all R/G/B channels
1077 * Usage: 6
1078 *
1079 * @param string A hexadecimal color code, #xxxxxx
1080 * @param integer Offset value 0-255 for all three channels.
1081 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1082 * @see modifyHTMLColor()
1083 */
1084 function modifyHTMLColorAll($color,$all) {
1085 return t3lib_div::modifyHTMLColor($color,$all,$all,$all);
1086 }
1087
1088 /**
1089 * Removes comma (if present) in the end of string
1090 * Usage: 2
1091 *
1092 * @param string String from which the comma in the end (if any) will be removed.
1093 * @return string
1094 */
1095 function rm_endcomma($string) {
1096 return ereg_replace(',$','',$string);
1097 }
1098
1099 /**
1100 * strtoupper which converts danish (and other characters) characters as well
1101 * Usage: 0
1102 *
1103 * @param string String to process
1104 * @return string
1105 * @deprecated Use t3lib_cs::conv_case() instead or for HTML output, wrap your content in <span class="uppercase">...</span>)
1106 * @ignore
1107 */
1108 function danish_strtoupper($string) {
1109 $value = strtoupper($string);
1110 return strtr($value, 'áéúíâêûôîæøåäöü', 'ÁÉÚÍÄËÜÖÏÆØÅÄÖÜ');
1111 }
1112
1113 /**
1114 * Change umlaut characters to plain ASCII with normally two character target
1115 * Only known characters will be converted, so don't expect a result for any character.
1116 *
1117 * ä => ae, Ö => Oe
1118 *
1119 * @param string String to convert.
1120 * @deprecated Works only for western europe single-byte charsets! Use t3lib_cs::specCharsToASCII() instead!
1121 * @return string
1122 */
1123 function convUmlauts($str) {
1124 $pat = array ( '/ä/', '/Ä/', '/ö/', '/Ö/', '/ü/', '/Ü/', '/ß/', '/å/', '/Å/', '/ø/', '/Ø/', '/æ/', '/Æ/' );
1125 $repl = array ( 'ae', 'Ae', 'oe', 'Oe', 'ue', 'Ue', 'ss', 'aa', 'AA', 'oe', 'OE', 'ae', 'AE' );
1126 return preg_replace($pat,$repl,$str);
1127 }
1128
1129 /**
1130 * Tests if the input is an integer.
1131 * Usage: 77
1132 *
1133 * @param mixed Any input variable to test.
1134 * @return boolean Returns true if string is an integer
1135 */
1136 function testInt($var) {
1137 return !strcmp($var,intval($var));
1138 }
1139
1140 /**
1141 * Returns true if the first part of $str matches the string $partStr
1142 * Usage: 59
1143 *
1144 * @param string Full string to check
1145 * @param string Reference string which must be found as the "first part" of the full string
1146 * @return boolean True if $partStr was found to be equal to the first part of $str
1147 */
1148 function isFirstPartOfStr($str,$partStr) {
1149 // Returns true, if the first part of a $str equals $partStr and $partStr is not ''
1150 $psLen = strlen($partStr);
1151 if ($psLen) {
1152 return substr($str,0,$psLen)==(string)$partStr;
1153 } else return false;
1154 }
1155
1156 /**
1157 * Formats the input integer $sizeInBytes as bytes/kilobytes/megabytes (-/K/M)
1158 * Usage: 53
1159 *
1160 * @param integer Number of bytes to format.
1161 * @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)
1162 * @return string Formatted representation of the byte number, for output.
1163 */
1164 function formatSize($sizeInBytes,$labels='') {
1165
1166 // Set labels:
1167 if (strlen($labels) == 0) {
1168 $labels = ' | K| M| G';
1169 } else {
1170 $labels = str_replace('"','',$labels);
1171 }
1172 $labelArr = explode('|',$labels);
1173
1174 // Find size:
1175 if ($sizeInBytes>900) {
1176 if ($sizeInBytes>900000000) { // GB
1177 $val = $sizeInBytes/(1024*1024*1024);
1178 return number_format($val, (($val<20)?1:0), '.', '').$labelArr[3];
1179 }
1180 elseif ($sizeInBytes>900000) { // MB
1181 $val = $sizeInBytes/(1024*1024);
1182 return number_format($val, (($val<20)?1:0), '.', '').$labelArr[2];
1183 } else { // KB
1184 $val = $sizeInBytes/(1024);
1185 return number_format($val, (($val<20)?1:0), '.', '').$labelArr[1];
1186 }
1187 } else { // Bytes
1188 return $sizeInBytes.$labelArr[0];
1189 }
1190 }
1191
1192 /**
1193 * Returns microtime input to milliseconds
1194 * Usage: 2
1195 *
1196 * @param string Microtime
1197 * @return integer Microtime input string converted to an integer (milliseconds)
1198 */
1199 function convertMicrotime($microtime) {
1200 $parts = explode(' ',$microtime);
1201 return round(($parts[0]+$parts[1])*1000);
1202 }
1203
1204 /**
1205 * This splits a string by the chars in $operators (typical /+-*) and returns an array with them in
1206 * Usage: 2
1207 *
1208 * @param string Input string, eg "123 + 456 / 789 - 4"
1209 * @param string Operators to split by, typically "/+-*"
1210 * @return array Array with operators and operands separated.
1211 * @see tslib_cObj::calc(), tslib_gifBuilder::calcOffset()
1212 */
1213 function splitCalc($string,$operators) {
1214 $res = Array();
1215 $sign='+';
1216 while($string) {
1217 $valueLen=strcspn($string,$operators);
1218 $value=substr($string,0,$valueLen);
1219 $res[] = Array($sign,trim($value));
1220 $sign=substr($string,$valueLen,1);
1221 $string=substr($string,$valueLen+1);
1222 }
1223 reset($res);
1224 return $res;
1225 }
1226
1227 /**
1228 * Calculates the input by +,-,*,/,%,^ with priority to + and -
1229 * Usage: 1
1230 *
1231 * @param string Input string, eg "123 + 456 / 789 - 4"
1232 * @return integer Calculated value. Or error string.
1233 * @see calcParenthesis()
1234 */
1235 function calcPriority($string) {
1236 $string=ereg_replace('[[:space:]]*','',$string); // removing all whitespace
1237 $string='+'.$string; // Ensuring an operator for the first entrance
1238 $qm='\*\/\+-^%';
1239 $regex = '(['.$qm.'])(['.$qm.']?[0-9\.]*)';
1240 // split the expression here:
1241 $reg = array();
1242 preg_match_all('/'.$regex.'/',$string,$reg);
1243
1244 reset($reg[2]);
1245 $number=0;
1246 $Msign='+';
1247 $err='';
1248 $buffer=doubleval(current($reg[2]));
1249 next($reg[2]); // Advance pointer
1250 while(list($k,$v)=each($reg[2])) {
1251 $v=doubleval($v);
1252 $sign = $reg[1][$k];
1253 if ($sign=='+' || $sign=='-') {
1254 $number = $Msign=='-' ? $number-=$buffer : $number+=$buffer;
1255 $Msign = $sign;
1256 $buffer=$v;
1257 } else {
1258 if ($sign=='/') {if ($v) $buffer/=$v; else $err='dividing by zero';}
1259 if ($sign=='%') {if ($v) $buffer%=$v; else $err='dividing by zero';}
1260 if ($sign=='*') {$buffer*=$v;}
1261 if ($sign=='^') {$buffer=pow($buffer,$v);}
1262 }
1263 }
1264 $number = $Msign=='-' ? $number-=$buffer : $number+=$buffer;
1265 return $err ? 'ERROR: '.$err : $number;
1266 }
1267
1268 /**
1269 * Calculates the input with parenthesis levels
1270 * Usage: 2
1271 *
1272 * @param string Input string, eg "(123 + 456) / 789 - 4"
1273 * @return integer Calculated value. Or error string.
1274 * @see calcPriority(), tslib_cObj::stdWrap()
1275 */
1276 function calcParenthesis($string) {
1277 $securC=100;
1278 do {
1279 $valueLenO=strcspn($string,'(');
1280 $valueLenC=strcspn($string,')');
1281 if ($valueLenC==strlen($string) || $valueLenC < $valueLenO) {
1282 $value = t3lib_div::calcPriority(substr($string,0,$valueLenC));
1283 $string = $value.substr($string,$valueLenC+1);
1284 return $string;
1285 } else {
1286 $string = substr($string,0,$valueLenO).t3lib_div::calcParenthesis(substr($string,$valueLenO+1));
1287 }
1288 // Security:
1289 $securC--;
1290 if ($securC<=0) break;
1291 } while($valueLenO<strlen($string));
1292 return $string;
1293 }
1294
1295 /**
1296 * Inverse version of htmlspecialchars()
1297 * Usage: 4
1298 *
1299 * @param string Value where &gt;, &lt;, &quot; and &amp; should be converted to regular chars.
1300 * @return string Converted result.
1301 */
1302 function htmlspecialchars_decode($value) {
1303 $value = str_replace('&gt;','>',$value);
1304 $value = str_replace('&lt;','<',$value);
1305 $value = str_replace('&quot;','"',$value);
1306 $value = str_replace('&amp;','&',$value);
1307 return $value;
1308 }
1309
1310 /**
1311 * Re-converts HTML entities if they have been converted by htmlspecialchars()
1312 * Usage: 10
1313 *
1314 * @param string String which contains eg. "&amp;amp;" which should stay "&amp;". Or "&amp;#1234;" to "&#1234;". Or "&amp;#x1b;" to "&#x1b;"
1315 * @return string Converted result.
1316 */
1317 function deHSCentities($str) {
1318 return ereg_replace('&amp;([#[:alnum:]]*;)','&\1',$str);
1319 }
1320
1321 /**
1322 * This function is used to escape any ' -characters when transferring text to JavaScript!
1323 * Usage: 3
1324 *
1325 * @param string String to escape
1326 * @param boolean If set, also backslashes are escaped.
1327 * @param string The character to escape, default is ' (single-quote)
1328 * @return string Processed input string
1329 */
1330 function slashJS($string,$extended=0,$char="'") {
1331 if ($extended) {$string = str_replace ("\\", "\\\\", $string);}
1332 return str_replace ($char, "\\".$char, $string);
1333 }
1334
1335 /**
1336 * Version of rawurlencode() where all spaces (%20) are re-converted to space-characters.
1337 * Usefull when passing text to JavaScript where you simply url-encode it to get around problems with syntax-errors, linebreaks etc.
1338 * Usage: 4
1339 *
1340 * @param string String to raw-url-encode with spaces preserved
1341 * @return string Rawurlencoded result of input string, but with all %20 (space chars) converted to real spaces.
1342 */
1343 function rawUrlEncodeJS($str) {
1344 return str_replace('%20',' ',rawurlencode($str));
1345 }
1346
1347 /**
1348 * rawurlencode which preserves "/" chars
1349 * Usefull when filepaths should keep the "/" chars, but have all other special chars encoded.
1350 * Usage: 5
1351 *
1352 * @param string Input string
1353 * @return string Output string
1354 */
1355 function rawUrlEncodeFP($str) {
1356 return str_replace('%2F','/',rawurlencode($str));
1357 }
1358
1359 /**
1360 * Checking syntax of input email address
1361 * Usage: 5
1362 *
1363 * @param string Input string to evaluate
1364 * @return boolean Returns true if the $email address (input string) is valid; Has a "@", domain name with at least one period and only allowed a-z characters.
1365 */
1366 function validEmail($email) {
1367 $email = trim ($email);
1368 if (strstr($email,' ')) return FALSE;
1369 return ereg('^[A-Za-z0-9\._-]+[@][A-Za-z0-9\._-]+[\.].[A-Za-z0-9]+$',$email) ? TRUE : FALSE;
1370 }
1371
1372 /**
1373 * Formats a string for output between <textarea>-tags
1374 * All content outputted in a textarea form should be passed through this function
1375 * 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!
1376 * Usage: 23
1377 *
1378 * @param string Input string to be formatted.
1379 * @return string Formatted for <textarea>-tags
1380 */
1381 function formatForTextarea($content) {
1382 return chr(10).htmlspecialchars($content);
1383 }
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396 /*************************
1397 *
1398 * ARRAY FUNCTIONS
1399 *
1400 *************************/
1401
1402 /**
1403 * Check if an item exists in an array
1404 * Please note that the order of parameters is reverse compared to the php4-function in_array()!!!
1405 * Usage: 3
1406 *
1407 * @param array one-dimensional array of items
1408 * @param string item to check for
1409 * @return boolean true if $item is in the one-dimensional array $in_array
1410 * @internal
1411 */
1412 function inArray($in_array,$item) {
1413 if (is_array($in_array)) {
1414 while (list(,$val)=each($in_array)) {
1415 if (!is_array($val) && !strcmp($val,$item)) return true;
1416 }
1417 }
1418 }
1419
1420 /**
1421 * Explodes a $string delimited by $delim and passes each item in the array through intval().
1422 * Corresponds to explode(), but with conversion to integers for all values.
1423 * Usage: 76
1424 *
1425 * @param string Delimiter string to explode with
1426 * @param string The string to explode
1427 * @return array Exploded values, all converted to integers
1428 */
1429 function intExplode($delim, $string) {
1430 $temp = explode($delim,$string);
1431 while(list($key,$val)=each($temp)) {
1432 $temp[$key]=intval($val);
1433 }
1434 reset($temp);
1435 return $temp;
1436 }
1437
1438 /**
1439 * Reverse explode which explodes the string counting from behind.
1440 * Thus t3lib_div::revExplode(':','my:words:here',2) will return array('my:words','here')
1441 * Usage: 8
1442 *
1443 * @param string Delimiter string to explode with
1444 * @param string The string to explode
1445 * @param integer Number of array entries
1446 * @return array Exploded values
1447 */
1448 function revExplode($delim, $string, $count=0) {
1449 $temp = explode($delim,strrev($string),$count);
1450 while(list($key,$val)=each($temp)) {
1451 $temp[$key]=strrev($val);
1452 }
1453 $temp=array_reverse($temp);
1454 reset($temp);
1455 return $temp;
1456 }
1457
1458 /**
1459 * Explodes a string and trims all values for whitespace in the ends.
1460 * If $onlyNonEmptyValues is set, then all blank ('') values are removed.
1461 * Usage: 256
1462 *
1463 * @param string Delimiter string to explode with
1464 * @param string The string to explode
1465 * @param boolean If set, all empty values (='') will NOT be set in output
1466 * @return array Exploded values
1467 */
1468 function trimExplode($delim, $string, $onlyNonEmptyValues=0) {
1469 $array = explode($delim, $string);
1470 // for two perfomance reasons the loop is duplicated
1471 // a) avoid check for $onlyNonEmptyValues in foreach loop
1472 // b) avoid unnecessary code when $onlyNonEmptyValues is not set
1473 if ($onlyNonEmptyValues) {
1474 $new_array = array();
1475 foreach($array as $value) {
1476 $value = trim($value);
1477 if ($value != '') {
1478 $new_array[] = $value;
1479 }
1480 }
1481 // direct return for perfomance reasons
1482 return $new_array;
1483 }
1484
1485 foreach($array as &$value) {
1486 $value = trim($value);
1487 }
1488
1489 return $array;
1490 }
1491
1492 /**
1493 * Remove duplicate values from an array
1494 * Usage: 0
1495 *
1496 * @param array Array of values to make unique
1497 * @return array
1498 * @ignore
1499 * @deprecated Use the PHP function array_unique instead
1500 */
1501 function uniqueArray($valueArray) {
1502 return array_unique($valueArray);
1503 }
1504
1505 /**
1506 * Removes the value $cmpValue from the $array if found there. Returns the modified array
1507 * Usage: 3
1508 *
1509 * @param array Array containing the values
1510 * @param string Value to search for and if found remove array entry where found.
1511 * @return array Output array with entries removed if search string is found
1512 */
1513 function removeArrayEntryByValue($array,$cmpValue) {
1514 if (is_array($array)) {
1515 reset($array);
1516 while(list($k,$v)=each($array)) {
1517 if (is_array($v)) {
1518 $array[$k] = t3lib_div::removeArrayEntryByValue($v,$cmpValue);
1519 } else {
1520 if (!strcmp($v,$cmpValue)) {
1521 unset($array[$k]);
1522 }
1523 }
1524 }
1525 }
1526 reset($array);
1527 return $array;
1528 }
1529
1530 /**
1531 * Implodes a multidim-array into GET-parameters (eg. &param[key][key2]=value2&param[key][key3]=value3)
1532 * Usage: 24
1533 *
1534 * @param string Name prefix for entries. Set to blank if you wish none.
1535 * @param array The (multidim) array to implode
1536 * @param string (keep blank)
1537 * @param boolean If set, parameters which were blank strings would be removed.
1538 * @param boolean If set, the param name itself (for example "param[key][key2]") would be rawurlencoded as well.
1539 * @return string Imploded result, fx. &param[key][key2]=value2&param[key][key3]=value3
1540 * @see explodeUrl2Array()
1541 */
1542 function implodeArrayForUrl($name,$theArray,$str='',$skipBlank=0,$rawurlencodeParamName=0) {
1543 if (is_array($theArray)) {
1544 foreach($theArray as $Akey => $AVal) {
1545 $thisKeyName = $name ? $name.'['.$Akey.']' : $Akey;
1546 if (is_array($AVal)) {
1547 $str = t3lib_div::implodeArrayForUrl($thisKeyName,$AVal,$str,$skipBlank,$rawurlencodeParamName);
1548 } else {
1549 if (!$skipBlank || strcmp($AVal,'')) {
1550 $str.='&'.($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName).
1551 '='.rawurlencode($AVal);
1552 }
1553 }
1554 }
1555 }
1556 return $str;
1557 }
1558
1559 /**
1560 * Explodes a string with GETvars (eg. "&id=1&type=2&ext[mykey]=3") into an array
1561 *
1562 * @param string GETvars string
1563 * @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())
1564 * @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!
1565 * @see implodeArrayForUrl()
1566 */
1567 function explodeUrl2Array($string,$multidim=FALSE) {
1568 $output = array();
1569 if ($multidim) {
1570 parse_str($string,$output);
1571 } else {
1572 $p = explode('&',$string);
1573 foreach($p as $v) {
1574 if (strlen($v)) {
1575 list($pK,$pV) = explode('=',$v,2);
1576 $output[rawurldecode($pK)] = rawurldecode($pV);
1577 }
1578 }
1579 }
1580 return $output;
1581 }
1582
1583 /**
1584 * Returns an array with selected keys from incoming data.
1585 * (Better read source code if you want to find out...)
1586 * Usage: 3
1587 *
1588 * @param string List of variable/key names
1589 * @param array Array from where to get values based on the keys in $varList
1590 * @param boolean If set, then t3lib_div::_GP() is used to fetch the value if not found (isset) in the $getArray
1591 * @return array Output array with selected variables.
1592 */
1593 function compileSelectedGetVarsFromArray($varList,$getArray,$GPvarAlt=1) {
1594 $keys = t3lib_div::trimExplode(',',$varList,1);
1595 $outArr=array();
1596 foreach($keys as $v) {
1597 if (isset($getArray[$v])) {
1598 $outArr[$v]=$getArray[$v];
1599 } elseif ($GPvarAlt) {
1600 $outArr[$v]=t3lib_div::_GP($v);
1601 }
1602 }
1603 return $outArr;
1604 }
1605
1606 /**
1607 * AddSlash array
1608 * This function traverses a multidimentional array and adds slashes to the values.
1609 * NOTE that the input array is and argument by reference.!!
1610 * Twin-function to stripSlashesOnArray
1611 * Usage: 8
1612 *
1613 * @param array Multidimensional input array, (REFERENCE!)
1614 * @return array
1615 */
1616 function addSlashesOnArray(&$theArray) {
1617 if (is_array($theArray)) {
1618 reset($theArray);
1619 while(list($Akey,$AVal)=each($theArray)) {
1620 if (is_array($AVal)) {
1621 t3lib_div::addSlashesOnArray($theArray[$Akey]);
1622 } else {
1623 $theArray[$Akey] = addslashes($AVal);
1624 }
1625 }
1626 reset($theArray);
1627 }
1628 }
1629
1630 /**
1631 * StripSlash array
1632 * This function traverses a multidimentional array and strips slashes to the values.
1633 * NOTE that the input array is and argument by reference.!!
1634 * Twin-function to addSlashesOnArray
1635 * Usage: 10
1636 *
1637 * @param array Multidimensional input array, (REFERENCE!)
1638 * @return array
1639 */
1640 function stripSlashesOnArray(&$theArray) {
1641 if (is_array($theArray)) {
1642 reset($theArray);
1643 while(list($Akey,$AVal)=each($theArray)) {
1644 if (is_array($AVal)) {
1645 t3lib_div::stripSlashesOnArray($theArray[$Akey]);
1646 } else {
1647 $theArray[$Akey] = stripslashes($AVal);
1648 }
1649 }
1650 reset($theArray);
1651 }
1652 }
1653
1654 /**
1655 * Either slashes ($cmd=add) or strips ($cmd=strip) array $arr depending on $cmd
1656 * Usage: 0
1657 *
1658 * @param array Multidimensional input array
1659 * @param string "add" or "strip", depending on usage you wish.
1660 * @return array
1661 */
1662 function slashArray($arr,$cmd) {
1663 if ($cmd=='strip') t3lib_div::stripSlashesOnArray($arr);
1664 if ($cmd=='add') t3lib_div::addSlashesOnArray($arr);
1665 return $arr;
1666 }
1667
1668 /**
1669 * Merges two arrays recursively and "binary safe" (integer keys are overridden as well), overruling similar values in the first array ($arr0) with the values of the second array ($arr1)
1670 * In case of identical keys, ie. keeping the values of the second.
1671 * Usage: 0
1672 *
1673 * @param array First array
1674 * @param array Second array, overruling the first array
1675 * @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.
1676 * @param boolean If set, values from $arr1 will overrule if they are empty or zero. Default: true
1677 * @return array Resulting array where $arr1 values has overruled $arr0 values
1678 */
1679 function array_merge_recursive_overrule($arr0,$arr1,$notAddKeys=0,$includeEmtpyValues=true) {
1680 reset($arr1);
1681 while(list($key,$val) = each($arr1)) {
1682 if(is_array($arr0[$key])) {
1683 if (is_array($arr1[$key])) {
1684 $arr0[$key] = t3lib_div::array_merge_recursive_overrule($arr0[$key],$arr1[$key],$notAddKeys,$includeEmtpyValues);
1685 }
1686 } else {
1687 if ($notAddKeys) {
1688 if (isset($arr0[$key])) {
1689 if ($includeEmtpyValues || $val) {
1690 $arr0[$key] = $val;
1691 }
1692 }
1693 } else {
1694 if ($includeEmtpyValues || $val) {
1695 $arr0[$key] = $val;
1696 }
1697 }
1698 }
1699 }
1700 reset($arr0);
1701 return $arr0;
1702 }
1703
1704 /**
1705 * 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.
1706 * Usage: 16
1707 *
1708 * @param array First array
1709 * @param array Second array
1710 * @return array Merged result.
1711 */
1712 function array_merge($arr1,$arr2) {
1713 return $arr2+$arr1;
1714 }
1715
1716 /**
1717 * Takes a row and returns a CSV string of the values with $delim (default is ,) and $quote (default is ") as separator chars.
1718 * Usage: 5
1719 *
1720 * @param array Input array of values
1721 * @param string Delimited, default is comman
1722 * @param string Quote-character to wrap around the values.
1723 * @return string A single line of CSV
1724 */
1725 function csvValues($row,$delim=',',$quote='"') {
1726 reset($row);
1727 $out=array();
1728 foreach ($row as $value) {
1729 $out[] = str_replace($quote, $quote.$quote, $value);
1730 }
1731 $str = $quote.implode($quote.$delim.$quote,$out).$quote;
1732 return $str;
1733 }
1734
1735 /**
1736 * Creates recursively a JSON literal from a mulidimensional associative array.
1737 * Uses Services_JSON (http://mike.teczno.com/JSON/doc/)
1738 *
1739 * @param array $jsonArray: The array to be transformed to JSON
1740 * @return string JSON string
1741 */
1742 function array2json($jsonArray) {
1743 if (!$GLOBALS['JSON']) {
1744 require_once(PATH_typo3.'contrib/json/json.php');
1745 $GLOBALS['JSON'] = t3lib_div::makeInstance('Services_JSON');
1746 }
1747 return $GLOBALS['JSON']->encode($jsonArray);
1748 }
1749
1750 /**
1751 * Removes dots "." from end of a key identifier of TypoScript styled array.
1752 * array('key.' => array('property.' => 'value')) --> array('key' => array('property' => 'value'))
1753 *
1754 * @param array $ts: TypoScript configuration array
1755 * @return array TypoScript configuration array without dots at the end of all keys
1756 */
1757 function removeDotsFromTS($ts) {
1758 $out = array();
1759 if (is_array($ts)) {
1760 foreach ($ts as $key => $value) {
1761 if (is_array($value)) {
1762 $key = rtrim($key, '.');
1763 $out[$key] = t3lib_div::removeDotsFromTS($value);
1764 } else {
1765 $out[$key] = $value;
1766 }
1767 }
1768 }
1769 return $out;
1770 }
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787 /*************************
1788 *
1789 * HTML/XML PROCESSING
1790 *
1791 *************************/
1792
1793 /**
1794 * Returns an array with all attributes of the input HTML tag as key/value pairs. Attributes are only lowercase a-z
1795 * $tag is either a whole tag (eg '<TAG OPTION ATTRIB=VALUE>') or the parameterlist (ex ' OPTION ATTRIB=VALUE>')
1796 * If a attribute is empty (I call it 'an option'), then the value for the key is empty. You can check if it existed with isset()
1797 * Usage: 8
1798 *
1799 * @param string HTML-tag string (or attributes only)
1800 * @return array Array with the attribute values.
1801 */
1802 function get_tag_attributes($tag) {
1803 $components = t3lib_div::split_tag_attributes($tag);
1804 $name = ''; // attribute name is stored here
1805 $valuemode = '';
1806 if (is_array($components)) {
1807 while (list($key,$val) = each ($components)) {
1808 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
1809 if ($valuemode) {
1810 if ($name) {
1811 $attributes[$name] = $val;
1812 $name = '';
1813 }
1814 } else {
1815 if ($key = strtolower(ereg_replace('[^a-zA-Z0-9]','',$val))) {
1816 $attributes[$key] = '';
1817 $name = $key;
1818 }
1819 }
1820 $valuemode = '';
1821 } else {
1822 $valuemode = 'on';
1823 }
1824 }
1825 if (is_array($attributes)) reset($attributes);
1826 return $attributes;
1827 }
1828 }
1829
1830 /**
1831 * Returns an array with the 'components' from an attribute list from an HTML tag. The result is normally analyzed by get_tag_attributes
1832 * Removes tag-name if found
1833 * Usage: 2
1834 *
1835 * @param string HTML-tag string (or attributes only)
1836 * @return array Array with the attribute values.
1837 * @internal
1838 */
1839 function split_tag_attributes($tag) {
1840 $tag_tmp = trim(eregi_replace ('^<[^[:space:]]*','',trim($tag)));
1841 // Removes any > in the end of the string
1842 $tag_tmp = trim(eregi_replace ('>$','',$tag_tmp));
1843
1844 while (strcmp($tag_tmp,'')) { // Compared with empty string instead , 030102
1845 $firstChar=substr($tag_tmp,0,1);
1846 if (!strcmp($firstChar,'"') || !strcmp($firstChar,"'")) {
1847 $reg=explode($firstChar,$tag_tmp,3);
1848 $value[]=$reg[1];
1849 $tag_tmp=trim($reg[2]);
1850 } elseif (!strcmp($firstChar,'=')) {
1851 $value[] = '=';
1852 $tag_tmp = trim(substr($tag_tmp,1)); // Removes = chars.
1853 } else {
1854 // There are '' around the value. We look for the next ' ' or '>'
1855 $reg = split('[[:space:]=]',$tag_tmp,2);
1856 $value[] = trim($reg[0]);
1857 $tag_tmp = trim(substr($tag_tmp,strlen($reg[0]),1).$reg[1]);
1858 }
1859 }
1860 if (is_array($value)) reset($value);
1861 return $value;
1862 }
1863
1864 /**
1865 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
1866 * Usage: 14
1867 *
1868 * @param array Array with attribute key/value pairs, eg. "bgcolor"=>"red", "border"=>0
1869 * @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!
1870 * @param boolean If true, don't check if values are blank. Default is to omit attributes with blank values.
1871 * @return string Imploded attributes, eg. 'bgcolor="red" border="0"'
1872 */
1873 function implodeAttributes($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE) {
1874 if (is_array($arr)) {
1875 if ($xhtmlSafe) {
1876 $newArr=array();
1877 foreach($arr as $p => $v) {
1878 if (!isset($newArr[strtolower($p)])) $newArr[strtolower($p)] = htmlspecialchars($v);
1879 }
1880 $arr = $newArr;
1881 }
1882 $list = array();
1883 foreach($arr as $p => $v) {
1884 if (strcmp($v,'') || $dontOmitBlankAttribs) {$list[]=$p.'="'.$v.'"';}
1885 }
1886 return implode(' ',$list);
1887 }
1888 }
1889
1890 /**
1891 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
1892 *
1893 * @param array See implodeAttributes()
1894 * @param boolean See implodeAttributes()
1895 * @param boolean See implodeAttributes()
1896 * @return string See implodeAttributes()
1897 * @deprecated Name was changed into implodeAttributes
1898 * @see implodeAttributes()
1899 */
1900 function implodeParams($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE) {
1901 return t3lib_div::implodeAttributes($arr,$xhtmlSafe,$dontOmitBlankAttribs);
1902 }
1903
1904 /**
1905 * Wraps JavaScript code XHTML ready with <script>-tags
1906 * Automatic re-identing of the JS code is done by using the first line as ident reference.
1907 * This is nice for identing JS code with PHP code on the same level.
1908 *
1909 * @param string JavaScript code
1910 * @param boolean Wrap script element in linebreaks? Default is TRUE.
1911 * @return string The wrapped JS code, ready to put into a XHTML page
1912 * @author Ingmar Schlecht <ingmars@web.de>
1913 * @author René Fritz <r.fritz@colorcube.de>
1914 */
1915 function wrapJS($string, $linebreak=TRUE) {
1916 if(trim($string)) {
1917 // <script wrapped in nl?
1918 $cr = $linebreak? "\n" : '';
1919
1920 // remove nl from the beginning
1921 $string = preg_replace ('/^\n+/', '', $string);
1922 // re-ident to one tab using the first line as reference
1923 $match = array();
1924 if(preg_match('/^(\t+)/',$string,$match)) {
1925 $string = str_replace($match[1],"\t", $string);
1926 }
1927 $string = $cr.'<script type="text/javascript">
1928 /*<![CDATA[*/
1929 '.$string.'
1930 /*]]>*/
1931 </script>'.$cr;
1932 }
1933 return trim($string);
1934 }
1935
1936
1937 /**
1938 * Parses XML input into a PHP array with associative keys
1939 * Usage: 0
1940 *
1941 * @param string XML data input
1942 * @param integer Number of element levels to resolve the XML into an array. Any further structure will be set as XML.
1943 * @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.
1944 * @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
1945 */
1946 function xml2tree($string,$depth=999) {
1947 $parser = xml_parser_create();
1948 $vals = array();
1949 $index = array();
1950
1951 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
1952 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
1953 xml_parse_into_struct($parser, $string, $vals, $index);
1954
1955 if (xml_get_error_code($parser)) return 'Line '.xml_get_current_line_number($parser).': '.xml_error_string(xml_get_error_code($parser));
1956 xml_parser_free($parser);
1957
1958 $stack = array( array() );
1959 $stacktop = 0;
1960 $startPoint=0;
1961
1962 // FIXME don't use unset() - what does that mean? Use NULL or similar.
1963 unset($tagi);
1964 foreach($vals as $key => $val) {
1965 $type = $val['type'];
1966
1967 // open tag:
1968 if ($type=='open' || $type=='complete') {
1969 $stack[$stacktop++] = $tagi;
1970
1971 if ($depth==$stacktop) {
1972 $startPoint=$key;
1973 }
1974
1975 $tagi = array('tag' => $val['tag']);
1976
1977 if(isset($val['attributes'])) $tagi['attrs'] = $val['attributes'];
1978 if(isset($val['value'])) $tagi['values'][] = $val['value'];
1979 }
1980 // finish tag:
1981 if ($type=='complete' || $type=='close') {
1982 $oldtagi = $tagi;
1983 $tagi = $stack[--$stacktop];
1984 $oldtag = $oldtagi['tag'];
1985 unset($oldtagi['tag']);
1986
1987 if ($depth==($stacktop+1)) {
1988 if ($key-$startPoint > 0) {
1989 $partArray = array_slice(
1990 $vals,
1991 $startPoint+1,
1992 $key-$startPoint-1
1993 );
1994 #$oldtagi=array('XMLvalue'=>t3lib_div::xmlRecompileFromStructValArray($partArray));
1995 $oldtagi['XMLvalue']=t3lib_div::xmlRecompileFromStructValArray($partArray);
1996 } else {
1997 $oldtagi['XMLvalue']=$oldtagi['values'][0];
1998 }
1999 }
2000
2001 $tagi['ch'][$oldtag][] = $oldtagi;
2002 unset($oldtagi);
2003 }
2004 // cdata
2005 if($type=='cdata') {
2006 $tagi['values'][] = $val['value'];
2007 }
2008 }
2009 return $tagi['ch'];
2010 }
2011
2012 /**
2013 * Turns PHP array into XML. See array2xml()
2014 *
2015 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2016 * @param string Alternative document tag. Default is "phparray".
2017 * @param array Options for the compilation. See array2xml() for description.
2018 * @param string Forced charset to prologue
2019 * @return string An XML string made from the input content in the array.
2020 * @see xml2array(),array2xml()
2021 */
2022 function array2xml_cs($array,$docTag='phparray',$options=array(),$charset='') {
2023
2024 // Figure out charset if not given explicitly:
2025 if (!$charset) {
2026 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { // First priority: forceCharset! If set, this will be authoritative!
2027 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
2028 } elseif (is_object($GLOBALS['LANG'])) {
2029 $charset = $GLOBALS['LANG']->charSet; // If "LANG" is around, that will hold the current charset
2030 } else {
2031 $charset = 'iso-8859-1'; // THIS is just a hopeful guess!
2032 }
2033 }
2034
2035 // Return XML:
2036 return '<?xml version="1.0" encoding="'.htmlspecialchars($charset).'" standalone="yes" ?>'.chr(10).
2037 t3lib_div::array2xml($array,'',0,$docTag,0, $options);
2038 }
2039
2040 /**
2041 * Deprecated to call directly (unless you are aware of using XML prologues)! Use "array2xml_cs" instead (which adds an XML-prologue)
2042 *
2043 * Converts a PHP array into an XML string.
2044 * The XML output is optimized for readability since associative keys are used as tagnames.
2045 * 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.
2046 * Numeric keys are stored with the default tagname "numIndex" but can be overridden to other formats)
2047 * 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
2048 * 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.
2049 * 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!
2050 * 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...
2051 * Usage: 5
2052 *
2053 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2054 * @param string tag-prefix, eg. a namespace prefix like "T3:"
2055 * @param integer Current recursion level. Don't change, stay at zero!
2056 * @param string Alternative document tag. Default is "phparray".
2057 * @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 chr(9) (TAB) is used
2058 * @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')
2059 * @param string Stack data. Don't touch.
2060 * @return string An XML string made from the input content in the array.
2061 * @see xml2array()
2062 */
2063 function array2xml($array,$NSprefix='',$level=0,$docTag='phparray',$spaceInd=0, $options=array(),$stackData=array()) {
2064 // 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
2065 $binaryChars = chr(0).chr(1).chr(2).chr(3).chr(4).chr(5).chr(6).chr(7).chr(8).
2066 chr(11).chr(12).chr(14).chr(15).chr(16).chr(17).chr(18).chr(19).
2067 chr(20).chr(21).chr(22).chr(23).chr(24).chr(25).chr(26).chr(27).chr(28).chr(29).
2068 chr(30).chr(31);
2069 // Set indenting mode:
2070 $indentChar = $spaceInd ? ' ' : chr(9);
2071 $indentN = $spaceInd>0 ? $spaceInd : 1;
2072 $nl = ($spaceInd >= 0 ? chr(10) : '');
2073
2074 // Init output variable:
2075 $output='';
2076
2077 // Traverse the input array
2078 if (is_array($array)) {
2079 foreach($array as $k=>$v) {
2080 $attr = '';
2081 $tagName = $k;
2082
2083 // Construct the tag name.
2084 if(isset($options['grandParentTagMap'][$stackData['grandParentTagName'].'/'.$stackData['parentTagName']])) { // Use tag based on grand-parent + parent tag name
2085 $attr.=' index="'.htmlspecialchars($tagName).'"';
2086 $tagName = (string)$options['grandParentTagMap'][$stackData['grandParentTagName'].'/'.$stackData['parentTagName']];
2087 }elseif(isset($options['parentTagMap'][$stackData['parentTagName'].':_IS_NUM']) && t3lib_div::testInt($tagName)) { // Use tag based on parent tag name + if current tag is numeric
2088 $attr.=' index="'.htmlspecialchars($tagName).'"';
2089 $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'].':_IS_NUM'];
2090 }elseif(isset($options['parentTagMap'][$stackData['parentTagName'].':'.$tagName])) { // Use tag based on parent tag name + current tag
2091 $attr.=' index="'.htmlspecialchars($tagName).'"';
2092 $tagName = (string)$options['parentTagMap'][$stackData['parentTagName'].':'.$tagName];
2093 } elseif(isset($options['parentTagMap'][$stackData['parentTagName']])) { // Use tag based on parent tag name:
2094 $attr.=' index="'.htmlspecialchars($tagName).'"';
2095 $tagName = (string)$options['parentTagMap'][$stackData['parentTagName']];
2096 } elseif (!strcmp(intval($tagName),$tagName)) { // If integer...;
2097 if ($options['useNindex']) { // If numeric key, prefix "n"
2098 $tagName = 'n'.$tagName;
2099 } else { // Use special tag for num. keys:
2100 $attr.=' index="'.$tagName.'"';
2101 $tagName = $options['useIndexTagForNum'] ? $options['useIndexTagForNum'] : 'numIndex';
2102 }
2103 } elseif($options['useIndexTagForAssoc']) { // Use tag for all associative keys:
2104 $attr.=' index="'.htmlspecialchars($tagName).'"';
2105 $tagName = $options['useIndexTagForAssoc'];
2106 }
2107
2108 // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
2109 $tagName = substr(ereg_replace('[^[:alnum:]_-]','',$tagName),0,100);
2110
2111 // If the value is an array then we will call this function recursively:
2112 if (is_array($v)) {
2113
2114 // Sub elements:
2115 if ($options['alt_options'][$stackData['path'].'/'.$tagName]) {
2116 $subOptions = $options['alt_options'][$stackData['path'].'/'.$tagName];
2117 $clearStackPath = $subOptions['clearStackPath'];
2118 } else {
2119 $subOptions = $options;
2120 $clearStackPath = FALSE;
2121 }
2122
2123 $content = $nl .
2124 t3lib_div::array2xml(
2125 $v,
2126 $NSprefix,
2127 $level+1,
2128 '',
2129 $spaceInd,
2130 $subOptions,
2131 array(
2132 'parentTagName' => $tagName,
2133 'grandParentTagName' => $stackData['parentTagName'],
2134 'path' => $clearStackPath ? '' : $stackData['path'].'/'.$tagName,
2135 )
2136 ).
2137 ($spaceInd >= 0 ? str_pad('',($level+1)*$indentN,$indentChar) : '');
2138 if ((int)$options['disableTypeAttrib']!=2) { // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
2139 $attr.=' type="array"';
2140 }
2141 } else { // Just a value:
2142
2143 // Look for binary chars:
2144 $vLen = strlen($v); // check for length, because PHP 5.2.0 may crash when first argument of strcspn is empty
2145 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!
2146 // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
2147 $content = $nl.chunk_split(base64_encode($v));
2148 $attr.=' base64="1"';
2149 } else {
2150 // Otherwise, just htmlspecialchar the stuff:
2151 $content = htmlspecialchars($v);
2152 $dType = gettype($v);
2153 if ($dType == 'string') {
2154 if ($options['useCDATA'] && $content != $v) {
2155 $content = '<![CDATA[' . $v . ']]>';
2156 }
2157 } elseif (!$options['disableTypeAttrib']) {
2158 $attr.= ' type="'.$dType.'"';
2159 }
2160 }
2161 }
2162
2163 // Add the element to the output string:
2164 $output.=($spaceInd >= 0 ? str_pad('',($level+1)*$indentN,$indentChar) : '').'<'.$NSprefix.$tagName.$attr.'>'.$content.'</'.$NSprefix.$tagName.'>'.$nl;
2165 }
2166 }
2167
2168 // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
2169 if (!$level) {
2170 $output =
2171 '<'.$docTag.'>'.$nl.
2172 $output.
2173 '</'.$docTag.'>';
2174 }
2175
2176 return $output;
2177 }
2178
2179 /**
2180 * Converts an XML string to a PHP array.
2181 * This is the reverse function of array2xml()
2182 * Usage: 17
2183 *
2184 * @param string XML content to convert into an array
2185 * @param string The tag-prefix resolve, eg. a namespace like "T3:"
2186 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array
2187 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content.
2188 * @see array2xml()
2189 */
2190 function xml2array($string,$NSprefix='',$reportDocTag=FALSE) {
2191 global $TYPO3_CONF_VARS;
2192
2193 // Create parser:
2194 $parser = xml_parser_create();
2195 $vals = array();
2196 $index = array();
2197
2198 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2199 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2200
2201 // PHP 5.0.0 & 5.0.1: default output charset is ISO-8859-1, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2202 // PHP 5.0.2+: default output charset is UTF-8 , only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2203 $match = array();
2204 preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/',substr($string,0,200),$match);
2205 $theCharset = $match[1] ? $match[1] : ($TYPO3_CONF_VARS['BE']['forceCharset'] ? $TYPO3_CONF_VARS['BE']['forceCharset'] : 'iso-8859-1');
2206 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1
2207
2208 // Parse content:
2209 xml_parse_into_struct($parser, $string, $vals, $index);
2210
2211 // If error, return error message:
2212 if (xml_get_error_code($parser)) {
2213 return 'Line '.xml_get_current_line_number($parser).': '.xml_error_string(xml_get_error_code($parser));
2214 }
2215 xml_parser_free($parser);
2216
2217 // Init vars:
2218 $stack = array(array());
2219 $stacktop = 0;
2220 $current=array();
2221 $tagName = '';
2222 $documentTag = '';
2223
2224 // Traverse the parsed XML structure:
2225 foreach($vals as $key => $val) {
2226
2227 // First, process the tag-name (which is used in both cases, whether "complete" or "close")
2228 $tagName = $val['tag'];
2229 if (!$documentTag) $documentTag = $tagName;
2230
2231 // Test for name space:
2232 $tagName = ($NSprefix && substr($tagName,0,strlen($NSprefix))==$NSprefix) ? substr($tagName,strlen($NSprefix)) : $tagName;
2233
2234 // Test for numeric tag, encoded on the form "nXXX":
2235 $testNtag = substr($tagName,1); // Closing tag.
2236 $tagName = (substr($tagName,0,1)=='n' && !strcmp(intval($testNtag),$testNtag)) ? intval($testNtag) : $tagName;
2237
2238 // Test for alternative index value:
2239 if (strlen($val['attributes']['index'])) { $tagName = $val['attributes']['index']; }
2240
2241 // Setting tag-values, manage stack:
2242 switch($val['type']) {
2243 case 'open': // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
2244 $current[$tagName] = array(); // Setting blank place holder
2245 $stack[$stacktop++] = $current;
2246 $current = array();
2247 break;
2248 case 'close': // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
2249 $oldCurrent = $current;
2250 $current = $stack[--$stacktop];
2251 end($current); // Going to the end of array to get placeholder key, key($current), and fill in array next:
2252 $current[key($current)] = $oldCurrent;
2253 unset($oldCurrent);
2254 break;
2255 case 'complete': // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
2256 if ($val['attributes']['base64']) {
2257 $current[$tagName] = base64_decode($val['value']);
2258 } else {
2259 $current[$tagName] = (string)$val['value']; // Had to cast it as a string - otherwise it would be evaluate false if tested with isset()!!
2260
2261 // Cast type:
2262 switch((string)$val['attributes']['type']) {
2263 case 'integer':
2264 $current[$tagName] = (integer)$current[$tagName];
2265 break;
2266 case 'double':
2267 $current[$tagName] = (double)$current[$tagName];
2268 break;
2269 case 'boolean':
2270 $current[$tagName] = (bool)$current[$tagName];
2271 break;
2272 case 'array':
2273 $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...
2274 break;
2275 }
2276 }
2277 break;
2278 }
2279 }
2280
2281 if ($reportDocTag) {
2282 $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
2283 }
2284
2285 // Finally return the content of the document tag.
2286 return $current[$tagName];
2287 }
2288
2289 /**
2290 * This implodes an array of XML parts (made with xml_parse_into_struct()) into XML again.
2291 * Usage: 2
2292 *
2293 * @param array A array of XML parts, see xml2tree
2294 * @return string Re-compiled XML data.
2295 */
2296 function xmlRecompileFromStructValArray($vals) {
2297 $XMLcontent='';
2298
2299 foreach($vals as $val) {
2300 $type = $val['type'];
2301
2302 // open tag:
2303 if ($type=='open' || $type=='complete') {
2304 $XMLcontent.='<'.$val['tag'];
2305 if(isset($val['attributes'])) {
2306 foreach($val['attributes'] as $k => $v) {
2307 $XMLcontent.=' '.$k.'="'.htmlspecialchars($v).'"';
2308 }
2309 }
2310 if ($type=='complete') {
2311 if(isset($val['value'])) {
2312 $XMLcontent.='>'.htmlspecialchars($val['value']).'</'.$val['tag'].'>';
2313 } else $XMLcontent.='/>';
2314 } else $XMLcontent.='>';
2315
2316 if ($type=='open' && isset($val['value'])) {
2317 $XMLcontent.=htmlspecialchars($val['value']);
2318 }
2319 }
2320 // finish tag:
2321 if ($type=='close') {
2322 $XMLcontent.='</'.$val['tag'].'>';
2323 }
2324 // cdata
2325 if($type=='cdata') {
2326 $XMLcontent.=htmlspecialchars($val['value']);
2327 }
2328 }
2329
2330 return $XMLcontent;
2331 }
2332
2333 /**
2334 * Extract the encoding scheme as found in the first line of an XML document (typically)
2335 * Usage: 1
2336 *
2337 * @param string XML data
2338 * @return string Encoding scheme (lowercase), if found.
2339 */
2340 function xmlGetHeaderAttribs($xmlData) {
2341 $xmlHeader = substr(trim($xmlData),0,200);
2342 $reg=array();
2343 if (eregi('^<\?xml([^>]*)\?\>',$xmlHeader,$reg)) {
2344 return t3lib_div::get_tag_attributes($reg[1]);
2345 }
2346 }
2347
2348 /**
2349 * Minifies JavaScript
2350 *
2351 * @param string $script Script to minify
2352 * @param string $error Error message (if any)
2353 * @return string Minified script or source string if error happened
2354 */
2355 function minifyJavaScript($script, &$error = '') {
2356 require_once(PATH_typo3 . 'contrib/jsmin/jsmin.php');
2357 try {
2358 $error = '';
2359 $script = trim(JSMin::minify(str_replace(chr(13), '', $script)));
2360 }
2361 catch(JSMinException $e) {
2362 $error = 'Error while minifying JavaScript: ' . $e->getMessage();
2363 t3lib_div::devLog($error, 't3lib_div', 2,
2364 array('JavaScript' => $script, 'Stack trace' => $e->getTrace()));
2365 }
2366 return $script;
2367 }
2368
2369
2370
2371
2372
2373
2374
2375
2376 /*************************
2377 *
2378 * FILES FUNCTIONS
2379 *
2380 *************************/
2381
2382 /**
2383 * Reads the file or url $url and returns the content
2384 * 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.
2385 * Usage: 83
2386 *
2387 * @param string Filepath/URL to read
2388 * @param integer Whether the HTTP header should be fetched or not. 0=disable, 1=fetch header+content, 2=fetch header only
2389 * @param array HTTP headers to be used in the request
2390 * @return string The content from the resource given as input.
2391 */
2392 function getURL($url, $includeHeader = 0, $requestHeaders = false) {
2393 $content = false;
2394
2395 // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>)
2396 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^https?:\/\//', $url)) {
2397 // External URL without error checking.
2398 $ch = curl_init();
2399 if (!$ch) {
2400 return false;
2401 }
2402
2403 curl_setopt($ch, CURLOPT_URL, $url);
2404 curl_setopt($ch, CURLOPT_HEADER, $includeHeader ? 1 : 0);
2405 curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0);
2406 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
2407 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2408 curl_setopt($ch, CURLOPT_FAILONERROR, 1);
2409 if (is_array($requestHeaders)) {
2410 curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2411 }
2412
2413 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
2414 curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
2415
2416 // Not sure if this is needed
2417 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
2418 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
2419 }
2420 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
2421 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
2422 }
2423 }
2424 $content = curl_exec($ch);
2425 curl_close($ch);
2426
2427 } elseif ($includeHeader) {
2428 $parsedURL = parse_url($url);
2429 if (!t3lib_div::inList('ftp,ftps,http,https,gopher,telnet', $parsedURL['scheme'])) {
2430 return false;
2431 }
2432
2433 $fp = @fsockopen($parsedURL['host'], ($parsedURL['port'] > 0 ? $parsedURL['port'] : 80), $errno, $errstr, 2.0);
2434 if (!$fp) {
2435 return false;
2436 }
2437
2438 $msg = 'GET ' . $parsedURL['path'] .
2439 ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') .
2440 ' HTTP/1.0' . "\r\n" . 'Host: ' .
2441 $parsedURL['host'] . "\r\n\r\n";
2442 fputs($fp, $msg);
2443 while (!feof($fp)) {
2444 $line = fgets($fp, 2048);
2445 $content.= $line;
2446 if ($includeHeader == 2 && !strlen(trim($line))) {
2447 break; // Stop at the first empty line (= end of header)
2448 }
2449 }
2450 fclose($fp);
2451
2452 } elseif (is_array($requestHeaders)) {
2453 $ctx = stream_context_create(array(
2454 'http' => array(
2455 'header' => implode("\r\n", $requestHeaders)
2456 )
2457 )
2458 );
2459 $content = @file_get_contents($url, false, $ctx);
2460 }
2461 else {
2462 $content = @file_get_contents($url);
2463 }
2464
2465 return $content;
2466 }
2467
2468 /**
2469 * Writes $content to the file $file
2470 * Usage: 30
2471 *
2472 * @param string Filepath to write to
2473 * @param string Content to write
2474 * @return boolean True if the file was successfully opened and written to.
2475 */
2476 function writeFile($file,$content) {
2477 if (!@is_file($file)) $changePermissions = true;
2478
2479 if ($fd = fopen($file,'wb')) {
2480 $res = fwrite($fd,$content);
2481 fclose($fd);
2482
2483 if ($res===false) return false;
2484
2485 if ($changePermissions) { // Change the permissions only if the file has just been created
2486 t3lib_div::fixPermissions($file);
2487 }
2488
2489 return true;
2490 }
2491
2492 return false;
2493 }
2494
2495 /**
2496 * Setting file system mode & group ownership of file
2497 *
2498 * @param string Filepath of newly created file
2499 * @return void
2500 */
2501 function fixPermissions($file) {
2502 if (@is_file($file) && TYPO3_OS!='WIN') {
2503 @chmod($file, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['fileCreateMask'])); // "@" is there because file is not necessarily OWNED by the user
2504 if($GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']) { // skip this if createGroup is empty
2505 @chgrp($file, $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']); // "@" is there because file is not necessarily OWNED by the user
2506 }
2507 }
2508 }
2509
2510 /**
2511 * Writes $content to a filename in the typo3temp/ folder (and possibly one or two subfolders...)
2512 * Accepts an additional subdirectory in the file path!
2513 *
2514 * @param string Absolute filepath to write to inside "typo3temp/". First part of this string must match PATH_site."typo3temp/"
2515 * @param string Content string to write
2516 * @return string Returns false on success, otherwise an error string telling about the problem.
2517 */
2518 function writeFileToTypo3tempDir($filepath,$content) {
2519
2520 // Parse filepath into directory and basename:
2521 $fI = pathinfo($filepath);
2522 $fI['dirname'].= '/';
2523
2524 // Check parts:
2525 if (t3lib_div::validPathStr($filepath) && $fI['basename'] && strlen($fI['basename'])<60) {
2526 if (defined('PATH_site')) {
2527 $dirName = PATH_site.'typo3temp/'; // Setting main temporary directory name (standard)
2528 if (@is_dir($dirName)) {
2529 if (t3lib_div::isFirstPartOfStr($fI['dirname'],$dirName)) {
2530
2531 // Checking if the "subdir" is found:
2532 $subdir = substr($fI['dirname'],strlen($dirName));
2533 if ($subdir) {
2534 if (ereg('^[[:alnum:]_]+\/$',$subdir) || ereg('^[[:alnum:]_]+\/[[:alnum:]_]+\/$',$subdir)) {
2535 $dirName.= $subdir;
2536 if (!@is_dir($dirName)) {
2537 t3lib_div::mkdir_deep(PATH_site.'typo3temp/', $subdir);
2538 }
2539 } else return 'Subdir, "'.$subdir.'", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"';
2540 }
2541 // Checking dir-name again (sub-dir might have been created):
2542 if (@is_dir($dirName)) {
2543 if ($filepath == $dirName.$fI['basename']) {
2544 t3lib_div::writeFile($filepath, $content);
2545 if (!@is_file($filepath)) return 'File not written to disk! Write permission error in filesystem?';
2546 } else return 'Calculated filelocation didn\'t match input $filepath!';
2547 } else return '"'.$dirName.'" is not a directory!';
2548 } else return '"'.$fI['dirname'].'" was not within directory PATH_site + "typo3temp/"';
2549 } else return 'PATH_site + "typo3temp/" was not a directory!';
2550 } else return 'PATH_site constant was NOT defined!';
2551 } else return 'Input filepath "'.$filepath.'" was generally invalid!';
2552 }
2553
2554 /**
2555 * Wrapper function for mkdir, setting folder permissions according to $GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'] and group ownership according to $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']
2556 * Usage: 6
2557 *
2558 * @param string Absolute path to folder, see PHP mkdir() function. Removes trailing slash internally.
2559 * @return boolean TRUE if @mkdir went well!
2560 */
2561 function mkdir($theNewFolder) {
2562 $theNewFolder = preg_replace('|/$|','',$theNewFolder);
2563 if (mkdir($theNewFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']))){
2564 chmod($theNewFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'])); //added this line, because the mode at 'mkdir' has a strange behaviour sometimes
2565
2566 if($GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']) { // skip this if createGroup is empty
2567 chgrp($theNewFolder, $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']);
2568 }
2569 return TRUE;
2570 }
2571 }
2572
2573 /**
2574 * Creates a directory - including parent directories if necessary - in the file system
2575 *
2576 * @param string Base folder. This must exist! Must have trailing slash! Example "/root/typo3site/"
2577 * @param string Deep directory to create, eg. "xx/yy/" which creates "/root/typo3site/xx/yy/" if $destination is "/root/typo3site/"
2578 * @return string If error, returns error string.
2579 */
2580 function mkdir_deep($destination,$deepDir) {
2581 $allParts = t3lib_div::trimExplode('/',$deepDir,1);
2582 $root = '';
2583 foreach($allParts as $part) {
2584 $root.= $part.'/';
2585 if (!is_dir($destination.$root)) {
2586 t3lib_div::mkdir($destination.$root);
2587 if (!@is_dir($destination.$root)) {
2588 return 'Error: The directory "'.$destination.$root.'" could not be created...';
2589 }
2590 }
2591 }
2592 }
2593
2594 /**
2595 * Wrapper function for rmdir, allowing recursive deletion of folders and files
2596 *
2597 * @param string Absolute path to folder, see PHP rmdir() function. Removes trailing slash internally.
2598 * @param boolean Allow deletion of non-empty directories
2599 * @return boolean true if @rmdir went well!
2600 */
2601 function rmdir($path,$removeNonEmpty=false) {
2602 $OK = false;
2603 $path = preg_replace('|/$|','',$path); // Remove trailing slash
2604
2605 if (file_exists($path)) {
2606 $OK = true;
2607
2608 if (is_dir($path)) {
2609 if ($removeNonEmpty==true && $handle = opendir($path)) {
2610 while ($OK && false !== ($file = readdir($handle))) {
2611 if ($file=='.' || $file=='..') continue;
2612 $OK = t3lib_div::rmdir($path.'/'.$file,$removeNonEmpty);
2613 }
2614 closedir($handle);
2615 }
2616 if ($OK) { $OK = rmdir($path); }
2617
2618 } else { // If $dirname is a file, simply remove it
2619 $OK = unlink($path);
2620 }
2621
2622 clearstatcache();
2623 }
2624
2625 return $OK;
2626 }
2627
2628 /**
2629 * Returns an array with the names of folders in a specific path
2630 * Will return 'error' (string) if there were an error with reading directory content.
2631 * Usage: 11
2632 *
2633 * @param string Path to list directories from
2634 * @return array Returns an array with the directory entries as values. If no path, the return value is nothing.
2635 */
2636 function get_dirs($path) {
2637 if ($path) {
2638 $d = @dir($path);
2639 if (is_object($d)) {
2640 while($entry=$d->read()) {
2641 if (@is_dir($path.'/'.$entry) && $entry!= '..' && $entry!= '.') {
2642 $filearray[]=$entry;
2643 }
2644 }
2645 $d->close();
2646 } else return 'error';
2647 return $filearray;
2648 }
2649 }
2650
2651 /**
2652 * Returns an array with the names of files in a specific path
2653 * Usage: 18
2654 *
2655 * @param string $path: Is the path to the file
2656 * @param string $extensionList is the comma list of extensions to read only (blank = all)
2657 * @param boolean If set, then the path is prepended the filenames. Otherwise only the filenames are returned in the array
2658 * @param string $order is sorting: 1= sort alphabetically, 'mtime' = sort by modification time.
2659 * @param string A comma seperated list of filenames to exclude, no wildcards
2660 * @return array Array of the files found
2661 */
2662 function getFilesInDir($path,$extensionList='',$prependPath=0,$order='',$excludePattern='') {
2663
2664 // Initialize variabels:
2665 $filearray = array();
2666 $sortarray = array();
2667 $path = ereg_replace('\/$','',$path);
2668
2669 // Find files+directories:
2670 if (@is_dir($path)) {
2671 $extensionList = strtolower($extensionList);
2672 $d = dir($path);
2673 if (is_object($d)) {
2674 while($entry=$d->read()) {
2675 if (@is_file($path.'/'.$entry)) {
2676 $fI = pathinfo($entry);
2677 $key = md5($path.'/'.$entry); // Don't change this ever - extensions may depend on the fact that the hash is an md5 of the path! (import/export extension)
2678 if ((!strlen($extensionList) || t3lib_div::inList($extensionList,strtolower($fI['extension']))) && (!strlen($excludePattern) || !preg_match('/^'.$excludePattern.'$/',$entry))) {
2679 $filearray[$key]=($prependPath?$path.'/':'').$entry;
2680 if ($order=='mtime') {$sortarray[$key]=filemtime($path.'/'.$entry);}
2681 elseif ($order) {$sortarray[$key]=$entry;}
2682 }
2683 }
2684 }
2685 $d->close();
2686 } else return 'error opening path: "'.$path.'"';
2687 }
2688
2689 // Sort them:
2690 if ($order) {
2691 asort($sortarray);
2692 reset($sortarray);
2693 $newArr=array();
2694 while(list($k,$v)=each($sortarray)) {
2695 $newArr[$k]=$filearray[$k];
2696 }
2697 $filearray=$newArr;
2698 }
2699
2700 // Return result
2701 reset($filearray);
2702 return $filearray;
2703 }
2704
2705 /**
2706 * Recursively gather all files and folders of a path.
2707 * Usage: 5
2708 *
2709 * @param array $fileArr: Empty input array (will have files added to it)
2710 * @param string $path: The path to read recursively from (absolute) (include trailing slash!)
2711 * @param string $extList: Comma list of file extensions: Only files with extensions in this list (if applicable) will be selected.
2712 * @param boolean $regDirs: If set, directories are also included in output.
2713 * @param integer $recursivityLevels: The number of levels to dig down...
2714 * @param string $excludePattern: regex pattern of files/directories to exclude
2715 * @return array An array with the found files/directories.
2716 */
2717 function getAllFilesAndFoldersInPath($fileArr,$path,$extList='',$regDirs=0,$recursivityLevels=99,$excludePattern='') {
2718 if ($regDirs) $fileArr[] = $path;
2719 $fileArr = array_merge($fileArr, t3lib_div::getFilesInDir($path,$extList,1,1,$excludePattern));
2720
2721 $dirs = t3lib_div::get_dirs($path);
2722 if (is_array($dirs) && $recursivityLevels>0) {
2723 foreach ($dirs as $subdirs) {
2724 if ((string)$subdirs!='' && (!strlen($excludePattern) || !preg_match('/^'.$excludePattern.'$/',$subdirs))) {
2725 $fileArr = t3lib_div::getAllFilesAndFoldersInPath($fileArr,$path.$subdirs.'/',$extList,$regDirs,$recursivityLevels-1,$excludePattern);
2726 }
2727 }
2728 }
2729 return $fileArr;
2730 }
2731
2732 /**
2733 * Removes the absolute part of all files/folders in fileArr
2734 * Usage: 2
2735 *
2736 * @param array $fileArr: The file array to remove the prefix from
2737 * @param string $prefixToRemove: The prefix path to remove (if found as first part of string!)
2738 * @return array The input $fileArr processed.
2739 */
2740 function removePrefixPathFromList($fileArr,$prefixToRemove) {
2741 foreach($fileArr as $k => $absFileRef) {
2742 if(t3lib_div::isFirstPartOfStr($absFileRef,$prefixToRemove)) {
2743 $fileArr[$k] = substr($absFileRef,strlen($prefixToRemove));
2744 } else return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!';
2745 }
2746 return $fileArr;
2747 }
2748
2749 /**
2750 * Fixes a path for windows-backslashes and reduces double-slashes to single slashes
2751 * Usage: 2
2752 *
2753 * @param string File path to process
2754 * @return string
2755 */
2756 function fixWindowsFilePath($theFile) {
2757 return str_replace('//','/', str_replace('\\','/', $theFile));
2758 }
2759
2760 /**
2761 * Resolves "../" sections in the input path string.
2762 * For example "fileadmin/directory/../other_directory/" will be resolved to "fileadmin/other_directory/"
2763 * Usage: 2
2764 *
2765 * @param string File path in which "/../" is resolved
2766 * @return string
2767 */
2768 function resolveBackPath($pathStr) {
2769 $parts = explode('/',$pathStr);
2770 $output=array();
2771 $c = 0;
2772 foreach($parts as $pV) {
2773 if ($pV=='..') {
2774 if ($c) {
2775 array_pop($output);
2776 $c--;
2777 } else $output[]=$pV;
2778 } else {
2779 $c++;
2780 $output[]=$pV;
2781 }
2782 }
2783 return implode('/',$output);
2784 }
2785
2786 /**
2787 * Prefixes a URL used with 'header-location' with 'http://...' depending on whether it has it already.
2788 * - If already having a scheme, nothing is prepended
2789 * - If having REQUEST_URI slash '/', then prefixing 'http://[host]' (relative to host)
2790 * - Otherwise prefixed with TYPO3_REQUEST_DIR (relative to current dir / TYPO3_REQUEST_DIR)
2791 * Usage: 30
2792 *
2793 * @param string URL / path to prepend full URL addressing to.
2794 * @return string
2795 */
2796 function locationHeaderUrl($path) {
2797 $uI = parse_url($path);
2798 if (substr($path,0,1)=='/') { // relative to HOST
2799 $path = t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST').$path;
2800 } elseif (!$uI['scheme']) { // No scheme either
2801 $path = t3lib_div::getIndpEnv('TYPO3_REQUEST_DIR').$path;
2802 }
2803 return $path;
2804 }
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821 /*************************
2822 *
2823 * DEBUG helper FUNCTIONS
2824 *
2825 *************************/
2826
2827 /**
2828 * Returns a string with a list of ascii-values for the first $characters characters in $string
2829 * Usage: 0
2830 *
2831 * @param string String to show ASCII value for
2832 * @param integer Number of characters to show
2833 * @return string The string with ASCII values in separated by a space char.
2834 * @internal
2835 */
2836 function debug_ordvalue($string,$characters=100) {
2837 if(strlen($string) < $characters) $characters = strlen($string);
2838 for ($i=0; $i<$characters; $i++) {
2839 $valuestring.=' '.ord(substr($string,$i,1));
2840 }
2841 return trim($valuestring);
2842 }
2843
2844 /**
2845 * Returns HTML-code, which is a visual representation of a multidimensional array
2846 * use t3lib_div::print_array() in order to print an array
2847 * Returns false if $array_in is not an array
2848 * Usage: 31
2849 *
2850 * @param array Array to view
2851 * @return string HTML output
2852 */
2853 function view_array($array_in) {
2854 if (is_array($array_in)) {
2855 $result='<table border="1" cellpadding="1" cellspacing="0" bgcolor="white">';
2856 if (!count($array_in)) {$result.= '<tr><td><font face="Verdana,Arial" size="1"><b>'.htmlspecialchars("EMPTY!").'</b></font></td></tr>';}
2857 while (list($key,$val)=each($array_in)) {
2858 $result.= '<tr><td valign="top"><font face="Verdana,Arial" size="1">'.htmlspecialchars((string)$key).'</font></td><td>';
2859 if (is_array($array_in[$key])) {
2860 $result.=t3lib_div::view_array($array_in[$key]);
2861 } elseif (is_object($array_in[$key])) {
2862 $result.= '[Object]';
2863 } else {
2864 $result.= '<font face="Verdana,Arial" size="1" color="red">'.nl2br(htmlspecialchars((string)$val)).'<br /></font>';
2865 }
2866 $result.= '</td></tr>';
2867 }
2868 $result.= '</table>';
2869 } else {
2870 $result = '<table border="1" cellpadding="1" cellspacing="0" bgcolor="white">
2871 <tr>
2872 <td><font face="Verdana,Arial" size="1" color="red">'.nl2br(htmlspecialchars((string)$array_in)).'<br /></font></td>
2873 </tr>
2874 </table>'; // Output it as a string.
2875 }
2876 return $result;
2877 }
2878
2879 /**
2880 * Prints an array
2881 * Usage: 6
2882 *
2883 * @param array Array to print visually (in a table).
2884 * @return void
2885 * @internal
2886 * @see view_array()
2887 */
2888 function print_array($array_in) {
2889 echo t3lib_div::view_array($array_in);
2890 }
2891
2892 /**
2893 * Makes debug output
2894 * Prints $var in bold between two vertical lines
2895 * If not $var the word 'debug' is printed
2896 * If $var is an array, the array is printed by t3lib_div::print_array()
2897 * Usage: 8
2898 *
2899 * @param mixed Variable to print
2900 * @param mixed If the parameter is a string it will be used as header. Otherwise number of break tags to apply after (positive integer) or before (negative integer) the output.
2901 * @return void
2902 */
2903 function debug($var='',$brOrHeader=0) {
2904 if ($brOrHeader && !t3lib_div::testInt($brOrHeader)) {
2905 echo '<table class="typo3-debug" border="0" cellpadding="0" cellspacing="0" bgcolor="white" style="border:0px; margin-top:3px; margin-bottom:3px;"><tr><td style="background-color:#bbbbbb; font-family: verdana,arial; font-weight: bold; font-size: 10px;">'.htmlspecialchars((string)$brOrHeader).'</td></tr><tr><td>';
2906 } elseif ($brOrHeader<0) {
2907 for($a=0;$a<abs(intval($brOrHeader));$a++){echo '<br />';}
2908 }
2909
2910 if (is_array($var)) {
2911 t3lib_div::print_array($var);
2912 } elseif (is_object($var)) {
2913 echo '<b>|Object:<pre>';
2914 print_r($var);
2915 echo '</pre>|</b>';
2916 } elseif ((string)$var!='') {
2917 echo '<b>|'.htmlspecialchars((string)$var).'|</b>';
2918 } else {
2919 echo '<b>| debug |</b>';
2920 }
2921
2922 if ($brOrHeader && !t3lib_div::testInt($brOrHeader)) {
2923 echo '</td></tr></table>';
2924 } elseif ($brOrHeader>0) {
2925 for($a=0;$a<intval($brOrHeader);$a++){echo '<br />';}
2926 }
2927 }
2928
2929 /**
2930 * Displays the "path" of the function call stack in a string, using debug_backtrace
2931 *
2932 * @return string
2933 */
2934 function debug_trail() {
2935 $trail = debug_backtrace();
2936 $trail = array_reverse($trail);
2937 array_pop($trail);
2938
2939 $path = array();
2940 foreach($trail as $dat) {
2941 $path[] = $dat['class'].$dat['type'].$dat['function'];
2942 }
2943
2944 return implode(' // ',$path);
2945 }
2946
2947 /**
2948 * Displays an array as rows in a table. Useful to debug output like an array of database records.
2949 *
2950 * @param array Array of arrays with similar keys
2951 * @param string Table header
2952 * @param boolean If TRUE, will return content instead of echo'ing out.
2953 * @return void Outputs to browser.
2954 */
2955 function debugRows($rows,$header='',$returnHTML=FALSE) {
2956 if (is_array($rows)) {
2957 reset($rows);
2958 $firstEl = current($rows);
2959 if (is_array($firstEl)) {
2960 $headerColumns = array_keys($firstEl);
2961 $tRows = array();
2962
2963 // Header:
2964 $tRows[] = '<tr><td colspan="'.count($headerColumns).'" style="background-color:#bbbbbb; font-family: verdana,arial; font-weight: bold; font-size: 10px;"><strong>'.htmlspecialchars($header).'</strong></td></tr>';
2965 $tCells = array();
2966 foreach($headerColumns as $key) {
2967 $tCells[] = '
2968 <td><font face="Verdana,Arial" size="1"><strong>'.htmlspecialchars($key).'</strong></font></td>';
2969 }
2970 $tRows[] = '
2971 <tr>'.implode('',$tCells).'
2972 </tr>';
2973
2974 // Rows:
2975 foreach($rows as $singleRow) {
2976 $tCells = array();
2977 foreach($headerColumns as $key) {
2978 $tCells[] = '
2979 <td><font face="Verdana,Arial" size="1">'.(is_array($singleRow[$key]) ? t3lib_div::debugRows($singleRow[$key],'',TRUE) : htmlspecialchars($singleRow[$key])).'</font></td>';
2980 }
2981 $tRows[] = '
2982 <tr>'.implode('',$tCells).'
2983 </tr>';
2984 }
2985
2986 $table = '
2987 <table border="1" cellpadding="1" cellspacing="0" bgcolor="white">'.implode('',$tRows).'
2988 </table>';
2989 if ($returnHTML) return $table; else echo $table;
2990 } else debug('Empty array of rows',$header);
2991 } else debug('No array of rows',$header);
2992 }
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021 /*************************
3022 *
3023 * SYSTEM INFORMATION
3024 *
3025 *************************/
3026
3027 /**
3028 * Returns the HOST+DIR-PATH of the current script (The URL, but without 'http://' and without script-filename)
3029 * Usage: 1
3030 *
3031 * @return string
3032 */
3033 function getThisUrl() {
3034 $p=parse_url(t3lib_div::getIndpEnv('TYPO3_REQUEST_SCRIPT')); // Url of this script
3035 $dir=t3lib_div::dirname($p['path']).'/'; // Strip file
3036 $url = str_replace('//','/',$p['host'].($p['port']?':'.$p['port']:'').$dir);
3037 return $url;
3038 }
3039
3040 /**
3041 * Returns the link-url to the current script.
3042 * In $getParams you can set associative keys corresponding to the GET-vars you wish to add to the URL. If you set them empty, they will remove existing GET-vars from the current URL.
3043 * REMEMBER to always use htmlspecialchars() for content in href-properties to get ampersands converted to entities (XHTML requirement and XSS precaution)
3044 * Usage: 52
3045 *
3046 * @param array Array of GET parameters to include
3047 * @return string
3048 */
3049 function linkThisScript($getParams=array()) {
3050 $parts = t3lib_div::getIndpEnv('SCRIPT_NAME');
3051 $params = t3lib_div::_GET();
3052
3053 foreach($getParams as $k => $v) {
3054 if (strcmp($v,'')) {
3055 $params[$k]=$v;
3056 } else unset($params[$k]);
3057 }
3058
3059 $pString = t3lib_div::implodeArrayForUrl('',$params);
3060
3061 return $pString ? $parts.'?'.ereg_replace('^&','',$pString) : $parts;
3062 }
3063
3064 /**
3065 * Takes a full URL, $url, possibly with a querystring and overlays the $getParams arrays values onto the quirystring, packs it all together and returns the URL again.
3066 * So basically it adds the parameters in $getParams to an existing URL, $url
3067 * Usage: 2
3068 *
3069 * @param string URL string
3070 * @param array Array of key/value pairs for get parameters to add/overrule with. Can be multidimensional.
3071 * @return string Output URL with added getParams.
3072 */
3073 function linkThisUrl($url,$getParams=array()) {
3074 $parts = parse_url($url);
3075 $getP = array();
3076 if ($parts['query']) {
3077 parse_str($parts['query'],$getP);
3078 }
3079 $getP = t3lib_div::array_merge_recursive_overrule($getP,$getParams);
3080 $uP = explode('?',$url);
3081
3082 $params = t3lib_div::implodeArrayForUrl('',$getP);
3083 $outurl = $uP[0].($params ? '?'.substr($params, 1) : '');
3084
3085 return $outurl;
3086 }
3087
3088 /**
3089 * Abstraction method which returns System Environment Variables regardless of server OS, CGI/MODULE version etc. Basically this is SERVER variables for most of them.
3090 * This should be used instead of getEnv() and $_SERVER/ENV_VARS to get reliable values for all situations.
3091 * Usage: 221
3092 *
3093 * @param string Name of the "environment variable"/"server variable" you wish to use. Valid values are SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY
3094 * @return string Value based on the input key, independent of server/os environment.
3095 */
3096 function getIndpEnv($getEnvName) {
3097 /*
3098 Conventions:
3099 output from parse_url():
3100 URL: http://username:password@192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value#link1
3101 [scheme] => 'http'
3102 [user] => 'username'
3103 [pass] => 'password'
3104 [host] => '192.168.1.4'
3105 [port] => '8080'
3106 [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/'
3107 [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value'
3108 [fragment] => 'link1'
3109
3110 Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php'
3111 [path_dir] = '/typo3/32/temp/phpcheck/'
3112 [path_info] = '/arg1/arg2/arg3/'
3113 [path] = [path_script/path_dir][path_info]
3114
3115
3116 Keys supported:
3117
3118 URI______:
3119 REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
3120 HTTP_HOST = [host][:[port]] = 192.168.1.4:8080
3121 SCRIPT_NAME = [path_script]++ = /typo3/32/temp/phpcheck/index.php // NOTICE THAT SCRIPT_NAME will return the php-script name ALSO. [path_script] may not do that (eg. '/somedir/' may result in SCRIPT_NAME '/somedir/index.php')!
3122 PATH_INFO = [path_info] = /arg1/arg2/arg3/
3123 QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value
3124 HTTP_REFERER = [scheme]://[host][:[port]][path] = http://192.168.1.4:8080/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value
3125 (Notice: NO username/password + NO fragment)
3126
3127 CLIENT____:
3128 REMOTE_ADDR = (client IP)
3129 REMOTE_HOST = (client host)
3130 HTTP_USER_AGENT = (client user agent)
3131 HTTP_ACCEPT_LANGUAGE = (client accept language)
3132
3133 SERVER____:
3134 SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\blabla\\blabl\\' will be converted to 'C:/blabla/blabl/'
3135
3136 Special extras:
3137 TYPO3_HOST_ONLY = [host] = 192.168.1.4
3138 TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value)
3139 TYPO3_REQUEST_HOST = [scheme]://[host][:[port]]
3140 TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different)
3141 TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script]
3142 TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir]
3143 TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend
3144 TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website
3145 TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically)
3146 TYPO3_SSL = Returns TRUE if this session uses SSL (HTTPS)
3147
3148 Notice: [fragment] is apparently NEVER available to the script!
3149
3150
3151 Testing suggestions:
3152 - Output all the values.
3153 - In the script, make a link to the script it self, maybe add some parameters and click the link a few times so HTTP_REFERER is seen
3154 - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!)
3155
3156 */
3157
3158 # if ($getEnvName=='HTTP_REFERER') return '';
3159
3160 $retVal = '';
3161
3162 switch ((string)$getEnvName) {
3163 case 'SCRIPT_NAME':
3164 $retVal = (php_sapi_name()=='cgi'||php_sapi_name()=='cgi-fcgi')&&($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) ? ($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) : ($_SERVER['ORIG_SCRIPT_NAME']?$_SERVER['ORIG_SCRIPT_NAME']:$_SERVER['SCRIPT_NAME']);
3165 break;
3166 case 'SCRIPT_FILENAME':
3167 $retVal = str_replace('//','/', str_replace('\\','/', (php_sapi_name()=='cgi'||php_sapi_name()=='isapi' ||php_sapi_name()=='cgi-fcgi')&&($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED'])? ($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED']):($_SERVER['ORIG_SCRIPT_FILENAME']?$_SERVER['ORIG_SCRIPT_FILENAME']:$_SERVER['SCRIPT_FILENAME'])));
3168 break;
3169 case 'REQUEST_URI':
3170 // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI'))
3171 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']) { // This is for URL rewriters that store the original URI in a server variable (eg ISAPI_Rewriter for IIS: HTTP_X_REWRITE_URL)
3172 list($v,$n) = explode('|',$GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']);
3173 $retVal = $GLOBALS[$v][$n];
3174 } elseif (!$_SERVER['REQUEST_URI']) { // This is for ISS/CGI which does not have the REQUEST_URI available.
3175 $retVal = '/'.ereg_replace('^/','',t3lib_div::getIndpEnv('SCRIPT_NAME')).
3176 ($_SERVER['QUERY_STRING']?'?'.$_SERVER['QUERY_STRING']:'');
3177 } else $retVal = $_SERVER['REQUEST_URI'];
3178 break;
3179 case 'PATH_INFO':
3180 // $_SERVER['PATH_INFO']!=$_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI) are seen to set PATH_INFO equal to script_name
3181 // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense.
3182 // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers, then 'php_sapi_name()=='cgi'' might be a better check. Right now strcmp($_SERVER['PATH_INFO'],t3lib_div::getIndpEnv('SCRIPT_NAME')) will always return false for CGI-versions, but that is only as long as SCRIPT_NAME is set equal to PATH_INFO because of php_sapi_name()=='cgi' (see above)
3183 // if (strcmp($_SERVER['PATH_INFO'],t3lib_div::getIndpEnv('SCRIPT_NAME')) && count(explode('/',$_SERVER['PATH_INFO']))>1) {
3184 if (php_sapi_name()!='cgi'&&php_sapi_name()!='cgi-fcgi') {
3185 $retVal = $_SERVER['PATH_INFO'];
3186 }
3187 break;
3188 // These are let through without modification
3189 case 'REMOTE_ADDR':
3190 case 'REMOTE_HOST':
3191 case 'HTTP_REFERER':
3192 case 'HTTP_HOST':
3193 case 'HTTP_USER_AGENT':
3194 case 'HTTP_ACCEPT_ENCODING':
3195 case 'HTTP_ACCEPT_LANGUAGE':
3196 case 'QUERY_STRING':
3197 $retVal = $_SERVER[$getEnvName];
3198 break;
3199 case 'TYPO3_DOCUMENT_ROOT':
3200 // Some CGI-versions (LA13CGI) and mod-rewrite rules on MODULE versions will deliver a 'wrong' DOCUMENT_ROOT (according to our description). Further various aliases/mod_rewrite rules can disturb this as well.
3201 // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME.
3202 $SFN = t3lib_div::getIndpEnv('SCRIPT_FILENAME');
3203 $SN_A = explode('/',strrev(t3lib_div::getIndpEnv('SCRIPT_NAME')));
3204 $SFN_A = explode('/',strrev($SFN));
3205 $acc = array();
3206 while(list($kk,$vv)=each($SN_A)) {
3207 if (!strcmp($SFN_A[$kk],$vv)) {
3208 $acc[] = $vv;
3209 } else break;
3210 }
3211 $commonEnd=strrev(implode('/',$acc));
3212 if (strcmp($commonEnd,'')) { $DR = substr($SFN,0,-(strlen($commonEnd)+1)); }
3213 $retVal = $DR;
3214 break;
3215 case 'TYPO3_HOST_ONLY':
3216 $p = explode(':',$_SERVER['HTTP_HOST']);
3217 $retVal = $p[0];
3218 break;
3219 case 'TYPO3_PORT':
3220 $p = explode(':',$_SERVER['HTTP_HOST']);
3221 $retVal = $p[1];
3222 break;
3223 case 'TYPO3_REQUEST_HOST':
3224 $retVal = (t3lib_div::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://').
3225 $_SERVER['HTTP_HOST'];
3226 break;
3227 case 'TYPO3_REQUEST_URL':
3228 $retVal = t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST').t3lib_div::getIndpEnv('REQUEST_URI');
3229 break;
3230 case 'TYPO3_REQUEST_SCRIPT':
3231 $retVal = t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST').t3lib_div::getIndpEnv('SCRIPT_NAME');
3232 break;
3233 case 'TYPO3_REQUEST_DIR':
3234 $retVal = t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST').t3lib_div::dirname(t3lib_div::getIndpEnv('SCRIPT_NAME')).'/';
3235 break;
3236 case 'TYPO3_SITE_URL':
3237 if (defined('PATH_thisScript') && defined('PATH_site')) {
3238 $lPath = substr(dirname(PATH_thisScript),strlen(PATH_site)).'/';
3239 $url = t3lib_div::getIndpEnv('TYPO3_REQUEST_DIR');
3240 $siteUrl = substr($url,0,-strlen($lPath));
3241 if (substr($siteUrl,-1)!='/') $siteUrl.='/';
3242 $retVal = $siteUrl;
3243 }
3244 break;
3245 case 'TYPO3_SITE_SCRIPT':
3246 $retVal = substr(t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'),strlen(t3lib_div::getIndpEnv('TYPO3_SITE_URL')));
3247 break;
3248 case 'TYPO3_SSL':
3249 $retVal = $_SERVER['SSL_SESSION_ID'] || !strcmp($_SERVER['HTTPS'],'on') || !strcmp($_SERVER['HTTPS'<