|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 /** 00028 * Contains the reknown class "t3lib_div" with general purpose functions 00029 * 00030 * $Id: class.t3lib_div.php 10545 2011-02-22 19:46:07Z lolli $ 00031 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj 00032 * XHTML compliant 00033 * Usage counts are based on search 22/2 2003 through whole source including tslib/ 00034 * 00035 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00036 */ 00037 /** 00038 * [CLASS/FUNCTION INDEX of SCRIPT] 00039 * 00040 * 00041 * 00042 * 232: class t3lib_div 00043 * 00044 * SECTION: GET/POST Variables 00045 * 262: function _GP($var) 00046 * 280: function _GET($var=NULL) 00047 * 297: function _POST($var=NULL) 00048 * 313: function _GETset($inputGet,$key='') 00049 * 336: function GPvar($var,$strip=0) 00050 * 353: function GParrayMerged($var) 00051 * 00052 * SECTION: IMAGE FUNCTIONS 00053 * 397: function gif_compress($theFile, $type) 00054 * 425: function png_to_gif_by_imagemagick($theFile) 00055 * 450: function read_png_gif($theFile,$output_png=0) 00056 * 00057 * SECTION: STRING FUNCTIONS 00058 * 499: function fixed_lgd($string,$origChars,$preStr='...') 00059 * 524: function fixed_lgd_pre($string,$chars) 00060 * 538: function fixed_lgd_cs($string,$chars) 00061 * 555: function breakTextForEmail($str,$implChar=LF,$charWidth=76) 00062 * 574: function breakLinesForEmail($str,$implChar=LF,$charWidth=76) 00063 * 610: function cmpIP($baseIP, $list) 00064 * 626: function cmpIPv4($baseIP, $list) 00065 * 668: function cmpIPv6($baseIP, $list) 00066 * 711: function IPv6Hex2Bin ($hex) 00067 * 726: function normalizeIPv6($address) 00068 * 782: function validIPv6($ip) 00069 * 805: function cmpFQDN($baseIP, $list) 00070 * 835: function inList($list,$item) 00071 * 847: function rmFromList($element,$list) 00072 * 863: function expandList($list) 00073 * 894: function intInRange($theInt,$min,$max=2000000000,$zeroValue=0) 00074 * 910: function intval_positive($theInt) 00075 * 923: function int_from_ver($verNumberStr) 00076 * 934: function compat_version($verNumberStr) 00077 * 952: function md5int($str) 00078 * 965: function shortMD5($input, $len=10) 00079 * 978: function uniqueList($in_list, $secondParameter=NULL) 00080 * 992: function split_fileref($fileref) 00081 * 1030: function dirname($path) 00082 * 1046: function modifyHTMLColor($color,$R,$G,$B) 00083 * 1066: function modifyHTMLColorAll($color,$all) 00084 * 1077: function rm_endcomma($string) 00085 * 1090: function danish_strtoupper($string) 00086 * 1105: function convUmlauts($str) 00087 * 1118: function testInt($var) 00088 * 1130: function isFirstPartOfStr($str,$partStr) 00089 * 1146: function formatSize($sizeInBytes,$labels='') 00090 * 1181: function convertMicrotime($microtime) 00091 * 1195: function splitCalc($string,$operators) 00092 * 1217: function calcPriority($string) 00093 * 1258: function calcParenthesis($string) 00094 * 1284: function htmlspecialchars_decode($value) 00095 * 1299: function deHSCentities($str) 00096 * 1312: function slashJS($string,$extended=0,$char="'") 00097 * 1325: function rawUrlEncodeJS($str) 00098 * 1337: function rawUrlEncodeFP($str) 00099 * 1348: function validEmail($email) 00100 * 1363: function formatForTextarea($content) 00101 * 00102 * SECTION: ARRAY FUNCTIONS 00103 * 1394: function inArray($in_array,$item) 00104 * 1411: function intExplode($delim, $string) 00105 * 1430: function revExplode($delim, $string, $count=0) 00106 * 1450: function trimExplode($delim, $string, $onlyNonEmptyValues=0) 00107 * 1472: function uniqueArray($valueArray) 00108 * 1484: function removeArrayEntryByValue($array,$cmpValue) 00109 * 1513: function implodeArrayForUrl($name,$theArray,$str='',$skipBlank=0,$rawurlencodeParamName=0) 00110 * 1538: function explodeUrl2Array($string,$multidim=FALSE) 00111 * 1564: function compileSelectedGetVarsFromArray($varList,$getArray,$GPvarAlt=1) 00112 * 1587: function addSlashesOnArray(&$theArray) 00113 * 1611: function stripSlashesOnArray(&$theArray) 00114 * 1633: function slashArray($arr,$cmd) 00115 * 1650: function array_merge_recursive_overrule($arr0,$arr1,$notAddKeys=0,$includeEmtpyValues=true) 00116 * 1683: function array_merge($arr1,$arr2) 00117 * 1696: function csvValues($row,$delim=',',$quote='"') 00118 * 00119 * SECTION: HTML/XML PROCESSING 00120 * 1738: function get_tag_attributes($tag) 00121 * 1775: function split_tag_attributes($tag) 00122 * 1809: function implodeAttributes($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE) 00123 * 1836: function implodeParams($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE) 00124 * 1851: function wrapJS($string, $linebreak=TRUE) 00125 * 1882: function xml2tree($string,$depth=999) 00126 * 1969: function array2xml($array,$NSprefix='',$level=0,$docTag='phparray',$spaceInd=0, $options=array(),$stackData=array()) 00127 * 2088: function xml2array($string,$NSprefix='',$reportDocTag=FALSE) 00128 * 2198: function xmlRecompileFromStructValArray($vals) 00129 * 2242: function xmlGetHeaderAttribs($xmlData) 00130 * 00131 * SECTION: FILES FUNCTIONS 00132 * 2275: function getURL($url, $includeHeader=0) 00133 * 2342: function writeFile($file,$content) 00134 * 2367: function fixPermissions($file) 00135 * 2384: function writeFileToTypo3tempDir($filepath,$content) 00136 * 2427: function mkdir($theNewFolder) 00137 * 2446: function mkdir_deep($destination,$deepDir) 00138 * 2468: function get_dirs($path) 00139 * 2493: function getFilesInDir($path,$extensionList='',$prependPath=0,$order='') 00140 * 2547: function getAllFilesAndFoldersInPath($fileArr,$path,$extList='',$regDirs=0,$recursivityLevels=99) 00141 * 2570: function removePrefixPathFromList($fileArr,$prefixToRemove) 00142 * 2586: function fixWindowsFilePath($theFile) 00143 * 2598: function resolveBackPath($pathStr) 00144 * 2626: function locationHeaderUrl($path) 00145 * 00146 * SECTION: DEBUG helper FUNCTIONS 00147 * 2666: function debug_ordvalue($string,$characters=100) 00148 * 2683: function view_array($array_in) 00149 * 2711: function print_array($array_in) 00150 * 2726: function debug($var="",$brOrHeader=0) 00151 * 2757: function debug_trail() 00152 * 2779: function debugRows($rows,$header='') 00153 * 00154 * SECTION: SYSTEM INFORMATION 00155 * 2857: function getThisUrl() 00156 * 2873: function linkThisScript($getParams=array()) 00157 * 2897: function linkThisUrl($url,$getParams=array()) 00158 * 2920: function getIndpEnv($getEnvName) 00159 * 3113: function milliseconds() 00160 * 3125: function clientInfo($useragent='') 00161 * 00162 * SECTION: TYPO3 SPECIFIC FUNCTIONS 00163 * 3212: function getFileAbsFileName($filename,$onlyRelative=1,$relToTYPO3_mainDir=0) 00164 * 3248: function validPathStr($theFile) 00165 * 3259: function isAbsPath($path) 00166 * 3270: function isAllowedAbsPath($path) 00167 * 3287: function verifyFilenameAgainstDenyPattern($filename) 00168 * 3305: function upload_copy_move($source,$destination) 00169 * 3331: function upload_to_tempfile($uploadedFileName) 00170 * 3349: function unlink_tempfile($uploadedTempFileName) 00171 * 3365: function tempnam($filePrefix) 00172 * 3379: function stdAuthCode($uid_or_record,$fields='',$codeLength=8) 00173 * 3410: function cHashParams($addQueryParams) 00174 * 3433: function hideIfNotTranslated($l18n_cfg_fieldValue) 00175 * 3448: function readLLfile($fileRef,$langKey) 00176 * 3472: function readLLXMLfile($fileRef,$langKey) 00177 * 3589: function llXmlAutoFileName($fileRef,$language) 00178 * 3633: function loadTCA($table) 00179 * 3653: function resolveSheetDefInDS($dataStructArray,$sheet='sDEF') 00180 * 3686: function resolveAllSheetsInDS($dataStructArray) 00181 * 3715: function callUserFunction($funcName,&$params,&$ref,$checkPrefix='user_',$silent=0) 00182 * 3813: function &getUserObj($classRef,$checkPrefix='user_',$silent=0) 00183 * 3871: function &makeInstance($className) 00184 * 3883: function makeInstanceClassName($className) 00185 * 3897: function &makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array()) 00186 * 3961: function plainMailEncoded($email,$subject,$message,$headers='',$enc='',$charset='',$dontEncodeHeader=false) 00187 * 4031: function quoted_printable($string,$maxlen=76) 00188 * 4078: function encodeHeader($line,$enc='',$charset='ISO-8859-1') 00189 * 4121: function substUrlsInPlainText($message,$urlmode='76',$index_script_url='') 00190 * 4155: function makeRedirectUrl($inUrl,$l=0,$index_script_url='') 00191 * 4182: function freetypeDpiComp($font_size) 00192 * 4194: function initSysLog() 00193 * 4251: function sysLog($msg, $extKey, $severity=0) 00194 * 4334: function devLog($msg, $extKey, $severity=0, $dataVar=FALSE) 00195 * 4355: function arrayToLogString($arr, $valueList=array(), $valueLength=20) 00196 * 4378: function imageMagickCommand($command, $parameters, $path='') 00197 * 4425: function unQuoteFilenames($parameters,$unQuote=FALSE) 00198 * 4459: function quoteJSvalue($value, $inScriptTags = false) 00199 * 00200 * TOTAL FUNCTIONS: 138 00201 * (This index is automatically created/updated by the extension "extdeveval") 00202 * 00203 */ 00204 00205 // a tabulator 00206 define('TAB', chr(9)); 00207 // a linefeed 00208 define('LF', chr(10)); 00209 // a carriage return 00210 define('CR', chr(13)); 00211 // a CR-LF combination 00212 define('CRLF', CR . LF); 00213 00214 /** 00215 * The legendary "t3lib_div" class - Miscellaneous functions for general purpose. 00216 * Most of the functions does not relate specifically to TYPO3 00217 * However a section of functions requires certain TYPO3 features available 00218 * See comments in the source. 00219 * You are encouraged to use this library in your own scripts! 00220 * 00221 * USE: 00222 * The class is intended to be used without creating an instance of it. 00223 * So: Don't instantiate - call functions with "t3lib_div::" prefixed the function name. 00224 * So use t3lib_div::[method-name] to refer to the functions, eg. 't3lib_div::milliseconds()' 00225 * 00226 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00227 * @package TYPO3 00228 * @subpackage t3lib 00229 */ 00230 final class t3lib_div { 00231 00232 // Severity constants used by t3lib_div::sysLog() 00233 const SYSLOG_SEVERITY_INFO = 0; 00234 const SYSLOG_SEVERITY_NOTICE = 1; 00235 const SYSLOG_SEVERITY_WARNING = 2; 00236 const SYSLOG_SEVERITY_ERROR = 3; 00237 const SYSLOG_SEVERITY_FATAL = 4; 00238 00239 /** 00240 * Singleton instances returned by makeInstance, using the class names as 00241 * array keys 00242 * 00243 * @var array<t3lib_Singleton> 00244 */ 00245 protected static $singletonInstances = array(); 00246 00247 /** 00248 * Instances returned by makeInstance, using the class names as array keys 00249 * 00250 * @var array<array><object> 00251 */ 00252 protected static $nonSingletonInstances = array(); 00253 00254 /** 00255 * Register for makeInstance with given class name and final class names to reduce number of class_exists() calls 00256 * 00257 * @var array Given class name => final class name 00258 */ 00259 protected static $finalClassNameRegister = array(); 00260 00261 /************************* 00262 * 00263 * GET/POST Variables 00264 * 00265 * Background: 00266 * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration. 00267 * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so. 00268 * But the clean solution is that quotes are never escaped and that is what the functions below offers. 00269 * Eventually TYPO3 should provide this in the global space as well. 00270 * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below. 00271 * 00272 *************************/ 00273 00274 /** 00275 * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order) 00276 * Strips slashes from all output, both strings and arrays. 00277 * 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! 00278 * Usage: 537 00279 * 00280 * @param string GET/POST var to return 00281 * @return mixed POST var named $var and if not set, the GET var of the same name. 00282 */ 00283 public static function _GP($var) { 00284 if (empty($var)) { 00285 return; 00286 } 00287 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var]; 00288 if (isset($value)) { 00289 if (is_array($value)) { 00290 self::stripSlashesOnArray($value); 00291 } else { 00292 $value = stripslashes($value); 00293 } 00294 } 00295 return $value; 00296 } 00297 00298 /** 00299 * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence. 00300 * 00301 * @param string Key (variable name) from GET or POST vars 00302 * @return array Returns the GET vars merged recursively onto the POST vars. 00303 */ 00304 public static function _GPmerged($parameter) { 00305 $postParameter = (isset($_POST[$parameter]) && is_array($_POST[$parameter])) ? $_POST[$parameter] : array(); 00306 $getParameter = (isset($_GET[$parameter]) && is_array($_GET[$parameter])) ? $_GET[$parameter] : array(); 00307 00308 $mergedParameters = self::array_merge_recursive_overrule($getParameter, $postParameter); 00309 self::stripSlashesOnArray($mergedParameters); 00310 00311 return $mergedParameters; 00312 } 00313 00314 /** 00315 * Returns the global $_GET array (or value from) normalized to contain un-escaped values. 00316 * ALWAYS use this API function to acquire the GET variables! 00317 * Usage: 27 00318 * 00319 * @param string Optional pointer to value in GET array (basically name of GET var) 00320 * @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!* 00321 * @see _POST(), _GP(), _GETset() 00322 */ 00323 public static function _GET($var = NULL) { 00324 $value = ($var === NULL) ? $_GET : (empty($var) ? NULL : $_GET[$var]); 00325 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting. 00326 if (is_array($value)) { 00327 self::stripSlashesOnArray($value); 00328 } else { 00329 $value = stripslashes($value); 00330 } 00331 } 00332 return $value; 00333 } 00334 00335 /** 00336 * Returns the global $_POST array (or value from) normalized to contain un-escaped values. 00337 * ALWAYS use this API function to acquire the $_POST variables! 00338 * Usage: 41 00339 * 00340 * @param string Optional pointer to value in POST array (basically name of POST var) 00341 * @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!* 00342 * @see _GET(), _GP() 00343 */ 00344 public static function _POST($var = NULL) { 00345 $value = ($var === NULL) ? $_POST : (empty($var) ? NULL : $_POST[$var]); 00346 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting. 00347 if (is_array($value)) { 00348 self::stripSlashesOnArray($value); 00349 } else { 00350 $value = stripslashes($value); 00351 } 00352 } 00353 return $value; 00354 } 00355 00356 /** 00357 * Writes input value to $_GET. 00358 * Usage: 2 00359 * 00360 * @param mixed $inputGet 00361 * array or single value to write to $_GET. Values should NOT be 00362 * escaped at input time (but will be escaped before writing 00363 * according to TYPO3 standards). 00364 * @param string $key 00365 * alternative key; If set, this will not set the WHOLE GET array, 00366 * but only the key in it specified by this value! 00367 * You can specify to replace keys on deeper array levels by 00368 * separating the keys with a pipe. 00369 * Example: 'parentKey|childKey' will result in 00370 * array('parentKey' => array('childKey' => $inputGet)) 00371 * 00372 * @return void 00373 */ 00374 public static function _GETset($inputGet, $key = '') { 00375 // adds slashes since TYPO3 standard currently is that slashes 00376 // must be applied (regardless of magic_quotes setting) 00377 if (is_array($inputGet)) { 00378 self::addSlashesOnArray($inputGet); 00379 } else { 00380 $inputGet = addslashes($inputGet); 00381 } 00382 00383 if ($key != '') { 00384 if (strpos($key, '|') !== FALSE) { 00385 $pieces = explode('|', $key); 00386 $newGet = array(); 00387 $pointer =& $newGet; 00388 foreach ($pieces as $piece) { 00389 $pointer =& $pointer[$piece]; 00390 } 00391 $pointer = $inputGet; 00392 $mergedGet = self::array_merge_recursive_overrule( 00393 $_GET, $newGet 00394 ); 00395 00396 $_GET = $mergedGet; 00397 $GLOBALS['HTTP_GET_VARS'] = $mergedGet; 00398 } else { 00399 $_GET[$key] = $inputGet; 00400 $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet; 00401 } 00402 } elseif (is_array($inputGet)) { 00403 $_GET = $inputGet; 00404 $GLOBALS['HTTP_GET_VARS'] = $inputGet; 00405 } 00406 } 00407 00408 /** 00409 * Wrapper for the RemoveXSS function. 00410 * Removes potential XSS code from an input string. 00411 * 00412 * Using an external class by Travis Puderbaugh <kallahar@quickwired.com> 00413 * 00414 * @param string Input string 00415 * @return string Input string with potential XSS code removed 00416 */ 00417 public static function removeXSS($string) { 00418 require_once(PATH_typo3 . 'contrib/RemoveXSS/RemoveXSS.php'); 00419 $string = RemoveXSS::process($string); 00420 return $string; 00421 } 00422 00423 00424 /************************* 00425 * 00426 * IMAGE FUNCTIONS 00427 * 00428 *************************/ 00429 00430 00431 /** 00432 * Compressing a GIF file if not already LZW compressed. 00433 * 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) 00434 * 00435 * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file 00436 * GIF: 00437 * 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!) 00438 * If $type is set to either 'IM' or 'GD' the compression is done with ImageMagick and GD respectively 00439 * PNG: 00440 * No changes. 00441 * 00442 * $theFile is expected to be a valid GIF-file! 00443 * The function returns a code for the operation. 00444 * Usage: 9 00445 * 00446 * @param string Filepath 00447 * @param string See description of function 00448 * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string. 00449 */ 00450 public static function gif_compress($theFile, $type) { 00451 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX']; 00452 $returnCode = ''; 00453 if ($gfxConf['gif_compress'] && strtolower(substr($theFile, -4, 4)) == '.gif') { // GIF... 00454 if (($type == 'IM' || !$type) && $gfxConf['im'] && $gfxConf['im_path_lzw']) { // IM 00455 // use temporary file to prevent problems with read and write lock on same file on network file systems 00456 $temporaryName = dirname($theFile) . '/' . md5(uniqid()) . '.gif'; 00457 // rename could fail, if a simultaneous thread is currently working on the same thing 00458 if (@rename($theFile, $temporaryName)) { 00459 $cmd = self::imageMagickCommand('convert', '"' . $temporaryName . '" "' . $theFile . '"', $gfxConf['im_path_lzw']); 00460 t3lib_utility_Command::exec($cmd); 00461 unlink($temporaryName); 00462 } 00463 00464 $returnCode = 'IM'; 00465 if (@is_file($theFile)) { 00466 self::fixPermissions($theFile); 00467 } 00468 } elseif (($type == 'GD' || !$type) && $gfxConf['gdlib'] && !$gfxConf['gdlib_png']) { // GD 00469 $tempImage = imageCreateFromGif($theFile); 00470 imageGif($tempImage, $theFile); 00471 imageDestroy($tempImage); 00472 $returnCode = 'GD'; 00473 if (@is_file($theFile)) { 00474 self::fixPermissions($theFile); 00475 } 00476 } 00477 } 00478 return $returnCode; 00479 } 00480 00481 /** 00482 * Converts a png file to gif. 00483 * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set true. 00484 * Usage: 5 00485 * 00486 * @param string $theFile the filename with path 00487 * @return string new filename 00488 */ 00489 public static function png_to_gif_by_imagemagick($theFile) { 00490 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] 00491 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] 00492 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw'] 00493 && strtolower(substr($theFile, -4, 4)) == '.png' 00494 && @is_file($theFile)) { // IM 00495 $newFile = substr($theFile, 0, -4) . '.gif'; 00496 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']); 00497 t3lib_utility_Command::exec($cmd); 00498 $theFile = $newFile; 00499 if (@is_file($newFile)) { 00500 self::fixPermissions($newFile); 00501 } 00502 // 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!! 00503 } 00504 return $theFile; 00505 } 00506 00507 /** 00508 * Returns filename of the png/gif version of the input file (which can be png or gif). 00509 * If input file type does not match the wanted output type a conversion is made and temp-filename returned. 00510 * Usage: 2 00511 * 00512 * @param string Filepath of image file 00513 * @param boolean If set, then input file is converted to PNG, otherwise to GIF 00514 * @return string If the new image file exists, it's filepath is returned 00515 */ 00516 public static function read_png_gif($theFile, $output_png = 0) { 00517 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] && @is_file($theFile)) { 00518 $ext = strtolower(substr($theFile, -4, 4)); 00519 if ( 00520 ((string) $ext == '.png' && $output_png) || 00521 ((string) $ext == '.gif' && !$output_png) 00522 ) { 00523 return $theFile; 00524 } else { 00525 $newFile = PATH_site . 'typo3temp/readPG_' . md5($theFile . '|' . filemtime($theFile)) . ($output_png ? '.png' : '.gif'); 00526 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path']); 00527 t3lib_utility_Command::exec($cmd); 00528 if (@is_file($newFile)) { 00529 self::fixPermissions($newFile); 00530 return $newFile; 00531 } 00532 } 00533 } 00534 } 00535 00536 00537 /************************* 00538 * 00539 * STRING FUNCTIONS 00540 * 00541 *************************/ 00542 00543 /** 00544 * Truncates a string with appended/prepended "..." and takes current character set into consideration. 00545 * Usage: 75 00546 * 00547 * @param string string to truncate 00548 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end. 00549 * @param string appendix to the truncated string 00550 * @return string cropped string 00551 */ 00552 public static function fixed_lgd_cs($string, $chars, $appendString = '...') { 00553 if (is_object($GLOBALS['LANG'])) { 00554 return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString); 00555 } elseif (is_object($GLOBALS['TSFE'])) { 00556 $charSet = ($GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet); 00557 return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString); 00558 } else { 00559 // this case should not happen 00560 $csConvObj = self::makeInstance('t3lib_cs'); 00561 return $csConvObj->crop('iso-8859-1', $string, $chars, $appendString); 00562 } 00563 } 00564 00565 /** 00566 * Breaks up a single line of text for emails 00567 * Usage: 5 00568 * 00569 * @param string The string to break up 00570 * @param string The string to implode the broken lines with (default/typically \n) 00571 * @param integer The line length 00572 * @return string 00573 */ 00574 public static function breakLinesForEmail($str, $implChar = LF, $charWidth = 76) { 00575 $lines = array(); 00576 $l = $charWidth; 00577 $p = 0; 00578 while (strlen($str) > $p) { 00579 $substr = substr($str, $p, $l); 00580 if (strlen($substr) == $l) { 00581 $count = count(explode(' ', trim(strrev($substr)))); 00582 if ($count > 1) { // OK... 00583 $parts = explode(' ', strrev($substr), 2); 00584 $theLine = strrev($parts[1]); 00585 } else { 00586 $afterParts = explode(' ', substr($str, $l + $p), 2); 00587 $theLine = $substr . $afterParts[0]; 00588 } 00589 if (!strlen($theLine)) { 00590 break; 00591 } // Error, because this would keep us in an endless loop. 00592 } else { 00593 $theLine = $substr; 00594 } 00595 00596 $lines[] = trim($theLine); 00597 $p += strlen($theLine); 00598 if (!trim(substr($str, $p, $l))) { 00599 break; 00600 } // added... 00601 } 00602 return implode($implChar, $lines); 00603 } 00604 00605 /** 00606 * Match IP number with list of numbers with wildcard 00607 * Dispatcher method for switching into specialised IPv4 and IPv6 methods. 00608 * Usage: 10 00609 * 00610 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR 00611 * @param string $list is a comma-list of IP-addresses to match with. *-wildcard allowed instead of number, plus leaving out parts in the IP number is accepted as wildcard (eg. 192.168.*.* equals 192.168). If list is "*" no check is done and the function returns TRUE immediately. An empty list always returns FALSE. 00612 * @return boolean True if an IP-mask from $list matches $baseIP 00613 */ 00614 public static function cmpIP($baseIP, $list) { 00615 $list = trim($list); 00616 if ($list === '') { 00617 return FALSE; 00618 } elseif ($list === '*') { 00619 return TRUE; 00620 } 00621 if (strpos($baseIP, ':') !== FALSE && self::validIPv6($baseIP)) { 00622 return self::cmpIPv6($baseIP, $list); 00623 } else { 00624 return self::cmpIPv4($baseIP, $list); 00625 } 00626 } 00627 00628 /** 00629 * Match IPv4 number with list of numbers with wildcard 00630 * 00631 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR 00632 * @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) 00633 * @return boolean True if an IP-mask from $list matches $baseIP 00634 */ 00635 public static function cmpIPv4($baseIP, $list) { 00636 $IPpartsReq = explode('.', $baseIP); 00637 if (count($IPpartsReq) == 4) { 00638 $values = self::trimExplode(',', $list, 1); 00639 00640 foreach ($values as $test) { 00641 list($test, $mask) = explode('/', $test); 00642 00643 if (intval($mask)) { 00644 // "192.168.3.0/24" 00645 $lnet = ip2long($test); 00646 $lip = ip2long($baseIP); 00647 $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT); 00648 $firstpart = substr($binnet, 0, $mask); 00649 $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT); 00650 $firstip = substr($binip, 0, $mask); 00651 $yes = (strcmp($firstpart, $firstip) == 0); 00652 } else { 00653 // "192.168.*.*" 00654 $IPparts = explode('.', $test); 00655 $yes = 1; 00656 foreach ($IPparts as $index => $val) { 00657 $val = trim($val); 00658 if (strcmp($val, '*') && strcmp($IPpartsReq[$index], $val)) { 00659 $yes = 0; 00660 } 00661 } 00662 } 00663 if ($yes) { 00664 return TRUE; 00665 } 00666 } 00667 } 00668 return FALSE; 00669 } 00670 00671 /** 00672 * Match IPv6 address with a list of IPv6 prefixes 00673 * 00674 * @param string $baseIP is the current remote IP address for instance 00675 * @param string $list is a comma-list of IPv6 prefixes, could also contain IPv4 addresses 00676 * @return boolean True if an baseIP matches any prefix 00677 */ 00678 public static function cmpIPv6($baseIP, $list) { 00679 $success = FALSE; // Policy default: Deny connection 00680 $baseIP = self::normalizeIPv6($baseIP); 00681 00682 $values = self::trimExplode(',', $list, 1); 00683 foreach ($values as $test) { 00684 list($test, $mask) = explode('/', $test); 00685 if (self::validIPv6($test)) { 00686 $test = self::normalizeIPv6($test); 00687 if (intval($mask)) { 00688 switch ($mask) { // test on /48 /64 00689 case '48': 00690 $testBin = substr(self::IPv6Hex2Bin($test), 0, 48); 00691 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 48); 00692 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE; 00693 break; 00694 case '64': 00695 $testBin = substr(self::IPv6Hex2Bin($test), 0, 64); 00696 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 64); 00697 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE; 00698 break; 00699 default: 00700 $success = FALSE; 00701 } 00702 } else { 00703 if (self::validIPv6($test)) { // test on full ip address 128 bits 00704 $testBin = self::IPv6Hex2Bin($test); 00705 $baseIPBin = self::IPv6Hex2Bin($baseIP); 00706 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE; 00707 } 00708 } 00709 } 00710 if ($success) { 00711 return TRUE; 00712 } 00713 } 00714 return FALSE; 00715 } 00716 00717 /** 00718 * [Describe function...] 00719 * 00720 * @param [type] $hex: ... 00721 * @return [type] ... 00722 */ 00723 public static function IPv6Hex2Bin($hex) { 00724 $bin = ''; 00725 $hex = str_replace(':', '', $hex); // Replace colon to nothing 00726 for ($i = 0; $i < strlen($hex); $i = $i + 2) { 00727 $bin .= chr(hexdec(substr($hex, $i, 2))); 00728 } 00729 return $bin; 00730 } 00731 00732 /** 00733 * Normalize an IPv6 address to full length 00734 * 00735 * @param string Given IPv6 address 00736 * @return string Normalized address 00737 */ 00738 public static function normalizeIPv6($address) { 00739 $normalizedAddress = ''; 00740 $stageOneAddress = ''; 00741 00742 $chunks = explode('::', $address); // Count 2 if if address has hidden zero blocks 00743 if (count($chunks) == 2) { 00744 $chunksLeft = explode(':', $chunks[0]); 00745 $chunksRight = explode(':', $chunks[1]); 00746 $left = count($chunksLeft); 00747 $right = count($chunksRight); 00748 00749 // Special case: leading zero-only blocks count to 1, should be 0 00750 if ($left == 1 && strlen($chunksLeft[0]) == 0) { 00751 $left = 0; 00752 } 00753 00754 $hiddenBlocks = 8 - ($left + $right); 00755 $hiddenPart = ''; 00756 $h = 0; 00757 while ($h < $hiddenBlocks) { 00758 $hiddenPart .= '0000:'; 00759 $h++; 00760 } 00761 00762 if ($left == 0) { 00763 $stageOneAddress = $hiddenPart . $chunks[1]; 00764 } else { 00765 $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1]; 00766 } 00767 } else { 00768 $stageOneAddress = $address; 00769 } 00770 00771 // normalize the blocks: 00772 $blocks = explode(':', $stageOneAddress); 00773 $divCounter = 0; 00774 foreach ($blocks as $block) { 00775 $tmpBlock = ''; 00776 $i = 0; 00777 $hiddenZeros = 4 - strlen($block); 00778 while ($i < $hiddenZeros) { 00779 $tmpBlock .= '0'; 00780 $i++; 00781 } 00782 $normalizedAddress .= $tmpBlock . $block; 00783 if ($divCounter < 7) { 00784 $normalizedAddress .= ':'; 00785 $divCounter++; 00786 } 00787 } 00788 return $normalizedAddress; 00789 } 00790 00791 /** 00792 * Validate a given IP address. 00793 * 00794 * Possible format are IPv4 and IPv6. 00795 * 00796 * @param string IP address to be tested 00797 * @return boolean True if $ip is either of IPv4 or IPv6 format. 00798 */ 00799 public static function validIP($ip) { 00800 return (filter_var($ip, FILTER_VALIDATE_IP) !== FALSE); 00801 } 00802 00803 /** 00804 * Validate a given IP address to the IPv4 address format. 00805 * 00806 * Example for possible format: 10.0.45.99 00807 * 00808 * @param string IP address to be tested 00809 * @return boolean True if $ip is of IPv4 format. 00810 */ 00811 public static function validIPv4($ip) { 00812 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE); 00813 } 00814 00815 /** 00816 * Validate a given IP address to the IPv6 address format. 00817 * 00818 * Example for possible format: 43FB::BB3F:A0A0:0 | ::1 00819 * 00820 * @param string IP address to be tested 00821 * @return boolean True if $ip is of IPv6 format. 00822 */ 00823 public static function validIPv6($ip) { 00824 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE); 00825 } 00826 00827 /** 00828 * Match fully qualified domain name with list of strings with wildcard 00829 * 00830 * @param string The current remote IP address for instance, typ. REMOTE_ADDR 00831 * @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) 00832 * @return boolean True if a domain name mask from $list matches $baseIP 00833 */ 00834 public static function cmpFQDN($baseIP, $list) { 00835 if (count(explode('.', $baseIP)) == 4) { 00836 $resolvedHostName = explode('.', gethostbyaddr($baseIP)); 00837 $values = self::trimExplode(',', $list, 1); 00838 00839 foreach ($values as $test) { 00840 $hostNameParts = explode('.', $test); 00841 $yes = 1; 00842 00843 foreach ($hostNameParts as $index => $val) { 00844 $val = trim($val); 00845 if (strcmp($val, '*') && strcmp($resolvedHostName[$index], $val)) { 00846 $yes = 0; 00847 } 00848 } 00849 if ($yes) { 00850 return TRUE; 00851 } 00852 } 00853 } 00854 return FALSE; 00855 } 00856 00857 /** 00858 * Checks if a given URL matches the host that currently handles this HTTP request. 00859 * Scheme, hostname and (optional) port of the given URL are compared. 00860 * 00861 * @param string $url: URL to compare with the TYPO3 request host 00862 * @return boolean Whether the URL matches the TYPO3 request host 00863 */ 00864 public static function isOnCurrentHost($url) { 00865 return (stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0); 00866 } 00867 00868 /** 00869 * Check for item in list 00870 * Check if an item exists in a comma-separated list of items. 00871 * Usage: 163 00872 * 00873 * @param string comma-separated list of items (string) 00874 * @param string item to check for 00875 * @return boolean true if $item is in $list 00876 */ 00877 public static function inList($list, $item) { 00878 return (strpos(',' . $list . ',', ',' . $item . ',') !== FALSE ? TRUE : FALSE); 00879 } 00880 00881 /** 00882 * Removes an item from a comma-separated list of items. 00883 * Usage: 1 00884 * 00885 * @param string element to remove 00886 * @param string comma-separated list of items (string) 00887 * @return string new comma-separated list of items 00888 */ 00889 public static function rmFromList($element, $list) { 00890 $items = explode(',', $list); 00891 foreach ($items as $k => $v) { 00892 if ($v == $element) { 00893 unset($items[$k]); 00894 } 00895 } 00896 return implode(',', $items); 00897 } 00898 00899 /** 00900 * Expand a comma-separated list of integers with ranges (eg 1,3-5,7 becomes 1,3,4,5,7). 00901 * Ranges are limited to 1000 values per range. 00902 * 00903 * @param string comma-separated list of integers with ranges (string) 00904 * @return string new comma-separated list of items 00905 * @author Martin Kutschker <martin.kutschker@activesolution.at> 00906 */ 00907 public static function expandList($list) { 00908 $items = explode(',', $list); 00909 $list = array(); 00910 foreach ($items as $item) { 00911 $range = explode('-', $item); 00912 if (isset($range[1])) { 00913 $runAwayBrake = 1000; 00914 for ($n = $range[0]; $n <= $range[1]; $n++) { 00915 $list[] = $n; 00916 00917 $runAwayBrake--; 00918 if ($runAwayBrake <= 0) { 00919 break; 00920 } 00921 } 00922 } else { 00923 $list[] = $item; 00924 } 00925 } 00926 return implode(',', $list); 00927 } 00928 00929 /** 00930 * Forces the integer $theInt into the boundaries of $min and $max. If the $theInt is 'false' then the $zeroValue is applied. 00931 * Usage: 224 00932 * 00933 * @param integer Input value 00934 * @param integer Lower limit 00935 * @param integer Higher limit 00936 * @param integer Default value if input is false. 00937 * @return integer The input value forced into the boundaries of $min and $max 00938 */ 00939 public static function intInRange($theInt, $min, $max = 2000000000, $zeroValue = 0) { 00940 // Returns $theInt as an integer in the integerspace from $min to $max 00941 $theInt = intval($theInt); 00942 if ($zeroValue && !$theInt) { 00943 $theInt = $zeroValue; 00944 } // If the input value is zero after being converted to integer, zeroValue may set another default value for it. 00945 if ($theInt < $min) { 00946 $theInt = $min; 00947 } 00948 if ($theInt > $max) { 00949 $theInt = $max; 00950 } 00951 return $theInt; 00952 } 00953 00954 /** 00955 * Returns the $integer if greater than zero, otherwise returns zero. 00956 * Usage: 1 00957 * 00958 * @param integer Integer string to process 00959 * @return integer 00960 */ 00961 public static function intval_positive($theInt) { 00962 $theInt = intval($theInt); 00963 if ($theInt < 0) { 00964 $theInt = 0; 00965 } 00966 return $theInt; 00967 } 00968 00969 /** 00970 * Returns an integer from a three part version number, eg '4.12.3' -> 4012003 00971 * Usage: 2 00972 * 00973 * @param string Version number on format x.x.x 00974 * @return integer Integer version of version number (where each part can count to 999) 00975 */ 00976 public static function int_from_ver($verNumberStr) { 00977 $verParts = explode('.', $verNumberStr); 00978 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)); 00979 } 00980 00981 /** 00982 * Returns true if the current TYPO3 version (or compatibility version) is compatible to the input version 00983 * Notice that this function compares branches, not versions (4.0.1 would be > 4.0.0 although they use the same compat_version) 00984 * 00985 * @param string Minimum branch number required (format x.y / e.g. "4.0" NOT "4.0.0"!) 00986 * @return boolean Returns true if this setup is compatible with the provided version number 00987 * @todo Still needs a function to convert versions to branches 00988 */ 00989 public static function compat_version($verNumberStr) { 00990 global $TYPO3_CONF_VARS; 00991 $currVersionStr = $TYPO3_CONF_VARS['SYS']['compat_version'] ? $TYPO3_CONF_VARS['SYS']['compat_version'] : TYPO3_branch; 00992 00993 if (self::int_from_ver($currVersionStr) < self::int_from_ver($verNumberStr)) { 00994 return FALSE; 00995 } else { 00996 return TRUE; 00997 } 00998 } 00999 01000 /** 01001 * Makes a positive integer hash out of the first 7 chars from the md5 hash of the input 01002 * Usage: 5 01003 * 01004 * @param string String to md5-hash 01005 * @return integer Returns 28bit integer-hash 01006 */ 01007 public static function md5int($str) { 01008 return hexdec(substr(md5($str), 0, 7)); 01009 } 01010 01011 /** 01012 * Returns the first 10 positions of the MD5-hash (changed from 6 to 10 recently) 01013 * 01014 * Usage: 37 01015 * 01016 * @param string Input string to be md5-hashed 01017 * @param integer The string-length of the output 01018 * @return string Substring of the resulting md5-hash, being $len chars long (from beginning) 01019 */ 01020 public static function shortMD5($input, $len = 10) { 01021 return substr(md5($input), 0, $len); 01022 } 01023 01024 /** 01025 * Returns a proper HMAC on a given input string and secret TYPO3 encryption key. 01026 * 01027 * @param string Input string to create HMAC from 01028 * @return string resulting (hexadecimal) HMAC currently with a length of 40 (HMAC-SHA-1) 01029 */ 01030 public static function hmac($input) { 01031 $hashAlgorithm = 'sha1'; 01032 $hashBlocksize = 64; 01033 $hmac = ''; 01034 01035 if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) { 01036 $hmac = hash_hmac($hashAlgorithm, $input, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']); 01037 } else { 01038 // outer padding 01039 $opad = str_repeat(chr(0x5C), $hashBlocksize); 01040 // innner padding 01041 $ipad = str_repeat(chr(0x36), $hashBlocksize); 01042 if (strlen($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) > $hashBlocksize) { 01043 // keys longer than blocksize are shorten 01044 $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])), $hashBlocksize, chr(0x00)); 01045 } else { 01046 // keys shorter than blocksize are zero-padded 01047 $key = str_pad($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $hashBlocksize, chr(0x00)); 01048 } 01049 $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, ($key ^ $ipad) . $input))); 01050 } 01051 return $hmac; 01052 } 01053 01054 /** 01055 * Takes comma-separated lists and arrays and removes all duplicates 01056 * If a value in the list is trim(empty), the value is ignored. 01057 * Usage: 16 01058 * 01059 * @param string Accept multiple parameters wich can be comma-separated lists of values and arrays. 01060 * @param mixed $secondParameter: Dummy field, which if set will show a warning! 01061 * @return string Returns the list without any duplicates of values, space around values are trimmed 01062 */ 01063 public static function uniqueList($in_list, $secondParameter = NULL) { 01064 if (is_array($in_list)) { 01065 throw new InvalidArgumentException( 01066 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support array arguments anymore! Only string comma lists!', 01067 1270853885 01068 ); 01069 } 01070 if (isset($secondParameter)) { 01071 throw new InvalidArgumentException( 01072 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one!', 01073 1270853886 01074 ); 01075 } 01076 01077 return implode(',', array_unique(self::trimExplode(',', $in_list, 1))); 01078 } 01079 01080 /** 01081 * Splits a reference to a file in 5 parts 01082 * Usage: 43 01083 * 01084 * @param string Filename/filepath to be analysed 01085 * @return array Contains keys [path], [file], [filebody], [fileext], [realFileext] 01086 */ 01087 public static function split_fileref($fileref) { 01088 $reg = array(); 01089 if (preg_match('/(.*\/)(.*)$/', $fileref, $reg)) { 01090 $info['path'] = $reg[1]; 01091 $info['file'] = $reg[2]; 01092 } else { 01093 $info['path'] = ''; 01094 $info['file'] = $fileref; 01095 } 01096 01097 $reg = ''; 01098 if (!is_dir($fileref) && preg_match('/(.*)\.([^\.]*$)/', $info['file'], $reg)) { 01099 $info['filebody'] = $reg[1]; 01100 $info['fileext'] = strtolower($reg[2]); 01101 $info['realFileext'] = $reg[2]; 01102 } else { 01103 $info['filebody'] = $info['file']; 01104 $info['fileext'] = ''; 01105 } 01106 reset($info); 01107 return $info; 01108 } 01109 01110 /** 01111 * Returns the directory part of a path without trailing slash 01112 * If there is no dir-part, then an empty string is returned. 01113 * Behaviour: 01114 * 01115 * '/dir1/dir2/script.php' => '/dir1/dir2' 01116 * '/dir1/' => '/dir1' 01117 * 'dir1/script.php' => 'dir1' 01118 * 'd/script.php' => 'd' 01119 * '/script.php' => '' 01120 * '' => '' 01121 * Usage: 5 01122 * 01123 * @param string Directory name / path 01124 * @return string Processed input value. See function description. 01125 */ 01126 public static function dirname($path) { 01127 $p = self::revExplode('/', $path, 2); 01128 return count($p) == 2 ? $p[0] : ''; 01129 } 01130 01131 /** 01132 * Modifies a HTML Hex color by adding/subtracting $R,$G and $B integers 01133 * Usage: 11 01134 * 01135 * @param string A hexadecimal color code, #xxxxxx 01136 * @param integer Offset value 0-255 01137 * @param integer Offset value 0-255 01138 * @param integer Offset value 0-255 01139 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars 01140 * @see modifyHTMLColorAll() 01141 */ 01142 public static function modifyHTMLColor($color, $R, $G, $B) { 01143 // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color 01144 $nR = self::intInRange(hexdec(substr($color, 1, 2)) + $R, 0, 255); 01145 $nG = self::intInRange(hexdec(substr($color, 3, 2)) + $G, 0, 255); 01146 $nB = self::intInRange(hexdec(substr($color, 5, 2)) + $B, 0, 255); 01147 return '#' . 01148 substr('0' . dechex($nR), -2) . 01149 substr('0' . dechex($nG), -2) . 01150 substr('0' . dechex($nB), -2); 01151 } 01152 01153 /** 01154 * Modifies a HTML Hex color by adding/subtracting $all integer from all R/G/B channels 01155 * Usage: 6 01156 * 01157 * @param string A hexadecimal color code, #xxxxxx 01158 * @param integer Offset value 0-255 for all three channels. 01159 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars 01160 * @see modifyHTMLColor() 01161 */ 01162 public static function modifyHTMLColorAll($color, $all) { 01163 return self::modifyHTMLColor($color, $all, $all, $all); 01164 } 01165 01166 /** 01167 * Removes comma (if present) in the end of string 01168 * Usage: 2 01169 * 01170 * @param string String from which the comma in the end (if any) will be removed. 01171 * @return string 01172 * @deprecated since TYPO3 4.5, will be removed in TYPO3 4.7 - Use rtrim() directly 01173 */ 01174 public static function rm_endcomma($string) { 01175 self::logDeprecatedFunction(); 01176 01177 return rtrim($string, ','); 01178 } 01179 01180 /** 01181 * Tests if the input can be interpreted as integer. 01182 * 01183 * @param mixed Any input variable to test 01184 * @return boolean Returns true if string is an integer 01185 */ 01186 public static function testInt($var) { 01187 if ($var === '') { 01188 return FALSE; 01189 } 01190 return (string) intval($var) === (string) $var; 01191 } 01192 01193 /** 01194 * Returns true if the first part of $str matches the string $partStr 01195 * Usage: 59 01196 * 01197 * @param string Full string to check 01198 * @param string Reference string which must be found as the "first part" of the full string 01199 * @return boolean True if $partStr was found to be equal to the first part of $str 01200 */ 01201 public static function isFirstPartOfStr($str, $partStr) { 01202 return $partStr != '' && strpos((string) $str, (string) $partStr, 0) === 0; 01203 } 01204 01205 /** 01206 * Formats the input integer $sizeInBytes as bytes/kilobytes/megabytes (-/K/M) 01207 * Usage: 53 01208 * 01209 * @param integer Number of bytes to format. 01210 * @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) 01211 * @return string Formatted representation of the byte number, for output. 01212 */ 01213 public static function formatSize($sizeInBytes, $labels = '') { 01214 01215 // Set labels: 01216 if (strlen($labels) == 0) { 01217 $labels = ' | K| M| G'; 01218 } else { 01219 $labels = str_replace('"', '', $labels); 01220 } 01221 $labelArr = explode('|', $labels); 01222 01223 // Find size: 01224 if ($sizeInBytes > 900) { 01225 if ($sizeInBytes > 900000000) { // GB 01226 $val = $sizeInBytes / (1024 * 1024 * 1024); 01227 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[3]; 01228 } 01229 elseif ($sizeInBytes > 900000) { // MB 01230 $val = $sizeInBytes / (1024 * 1024); 01231 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[2]; 01232 } else { // KB 01233 $val = $sizeInBytes / (1024); 01234 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[1]; 01235 } 01236 } else { // Bytes 01237 return $sizeInBytes . $labelArr[0]; 01238 } 01239 } 01240 01241 /** 01242 * Returns microtime input to milliseconds 01243 * Usage: 2 01244 * 01245 * @param string Microtime 01246 * @return integer Microtime input string converted to an integer (milliseconds) 01247 */ 01248 public static function convertMicrotime($microtime) { 01249 $parts = explode(' ', $microtime); 01250 return round(($parts[0] + $parts[1]) * 1000); 01251 } 01252 01253 /** 01254 * This splits a string by the chars in $operators (typical /+-*) and returns an array with them in 01255 * Usage: 2 01256 * 01257 * @param string Input string, eg "123 + 456 / 789 - 4" 01258 * @param string Operators to split by, typically "/+-*" 01259 * @return array Array with operators and operands separated. 01260 * @see tslib_cObj::calc(), tslib_gifBuilder::calcOffset() 01261 */ 01262 public static function splitCalc($string, $operators) { 01263 $res = Array(); 01264 $sign = '+'; 01265 while ($string) { 01266 $valueLen = strcspn($string, $operators); 01267 $value = substr($string, 0, $valueLen); 01268 $res[] = Array($sign, trim($value)); 01269 $sign = substr($string, $valueLen, 1); 01270 $string = substr($string, $valueLen + 1); 01271 } 01272 reset($res); 01273 return $res; 01274 } 01275 01276 /** 01277 * Calculates the input by +,-,*,/,%,^ with priority to + and - 01278 * Usage: 1 01279 * 01280 * @param string Input string, eg "123 + 456 / 789 - 4" 01281 * @return integer Calculated value. Or error string. 01282 * @see calcParenthesis() 01283 */ 01284 public static function calcPriority($string) { 01285 $string = preg_replace('/[[:space:]]*/', '', $string); // removing all whitespace 01286 $string = '+' . $string; // Ensuring an operator for the first entrance 01287 $qm = '\*\/\+-^%'; 01288 $regex = '([' . $qm . '])([' . $qm . ']?[0-9\.]*)'; 01289 // split the expression here: 01290 $reg = array(); 01291 preg_match_all('/' . $regex . '/', $string, $reg); 01292 01293 reset($reg[2]); 01294 $number = 0; 01295 $Msign = '+'; 01296 $err = ''; 01297 $buffer = doubleval(current($reg[2])); 01298 next($reg[2]); // Advance pointer 01299 01300 while (list($k, $v) = each($reg[2])) { 01301 $v = doubleval($v); 01302 $sign = $reg[1][$k]; 01303 if ($sign == '+' || $sign == '-') { 01304 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer; 01305 $Msign = $sign; 01306 $buffer = $v; 01307 } else { 01308 if ($sign == '/') { 01309 if ($v) { 01310 $buffer /= $v; 01311 } else { 01312 $err = 'dividing by zero'; 01313 } 01314 } 01315 if ($sign == '%') { 01316 if ($v) { 01317 $buffer %= $v; 01318 } else { 01319 $err = 'dividing by zero'; 01320 } 01321 } 01322 if ($sign == '*') { 01323 $buffer *= $v; 01324 } 01325 if ($sign == '^') { 01326 $buffer = pow($buffer, $v); 01327 } 01328 } 01329 } 01330 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer; 01331 return $err ? 'ERROR: ' . $err : $number; 01332 } 01333 01334 /** 01335 * Calculates the input with parenthesis levels 01336 * Usage: 2 01337 * 01338 * @param string Input string, eg "(123 + 456) / 789 - 4" 01339 * @return integer Calculated value. Or error string. 01340 * @see calcPriority(), tslib_cObj::stdWrap() 01341 */ 01342 public static function calcParenthesis($string) { 01343 $securC = 100; 01344 do { 01345 $valueLenO = strcspn($string, '('); 01346 $valueLenC = strcspn($string, ')'); 01347 if ($valueLenC == strlen($string) || $valueLenC < $valueLenO) { 01348 $value = self::calcPriority(substr($string, 0, $valueLenC)); 01349 $string = $value . substr($string, $valueLenC + 1); 01350 return $string; 01351 } else { 01352 $string = substr($string, 0, $valueLenO) . self::calcParenthesis(substr($string, $valueLenO + 1)); 01353 } 01354 // Security: 01355 $securC--; 01356 if ($securC <= 0) { 01357 break; 01358 } 01359 } while ($valueLenO < strlen($string)); 01360 return $string; 01361 } 01362 01363 /** 01364 * Inverse version of htmlspecialchars() 01365 * Usage: 4 01366 * 01367 * @param string Value where >, <, " and & should be converted to regular chars. 01368 * @return string Converted result. 01369 */ 01370 public static function htmlspecialchars_decode($value) { 01371 $value = str_replace('>', '>', $value); 01372 $value = str_replace('<', '<', $value); 01373 $value = str_replace('"', '"', $value); 01374 $value = str_replace('&', '&', $value); 01375 return $value; 01376 } 01377 01378 /** 01379 * Re-converts HTML entities if they have been converted by htmlspecialchars() 01380 * Usage: 10 01381 * 01382 * @param string String which contains eg. "&amp;" which should stay "&". Or "&#1234;" to "Ӓ". Or "&#x1b;" to "" 01383 * @return string Converted result. 01384 */ 01385 public static function deHSCentities($str) { 01386 return preg_replace('/&([#[:alnum:]]*;)/', '&\1', $str); 01387 } 01388 01389 /** 01390 * This function is used to escape any ' -characters when transferring text to JavaScript! 01391 * Usage: 3 01392 * 01393 * @param string String to escape 01394 * @param boolean If set, also backslashes are escaped. 01395 * @param string The character to escape, default is ' (single-quote) 01396 * @return string Processed input string 01397 */ 01398 public static function slashJS($string, $extended = 0, $char = "'") { 01399 if ($extended) { 01400 $string = str_replace("\\", "\\\\", $string); 01401 } 01402 return str_replace($char, "\\" . $char, $string); 01403 } 01404 01405 /** 01406 * Version of rawurlencode() where all spaces (%20) are re-converted to space-characters. 01407 * Usefull when passing text to JavaScript where you simply url-encode it to get around problems with syntax-errors, linebreaks etc. 01408 * Usage: 4 01409 * 01410 * @param string String to raw-url-encode with spaces preserved 01411 * @return string Rawurlencoded result of input string, but with all %20 (space chars) converted to real spaces. 01412 */ 01413 public static function rawUrlEncodeJS($str) { 01414 return str_replace('%20', ' ', rawurlencode($str)); 01415 } 01416 01417 /** 01418 * rawurlencode which preserves "/" chars 01419 * Usefull when filepaths should keep the "/" chars, but have all other special chars encoded. 01420 * Usage: 5 01421 * 01422 * @param string Input string 01423 * @return string Output string 01424 */ 01425 public static function rawUrlEncodeFP($str) { 01426 return str_replace('%2F', '/', rawurlencode($str)); 01427 } 01428 01429 /** 01430 * Checking syntax of input email address 01431 * Usage: 5 01432 * 01433 * @param string Input string to evaluate 01434 * @return boolean Returns true if the $email address (input string) is valid 01435 */ 01436 public static function validEmail($email) { 01437 // enforce maximum length to prevent libpcre recursion crash bug #52929 in PHP 01438 // fixed in PHP 5.2+ later than Sept 2010; length restriction per SMTP RFC 2821 01439 if (strlen($email) > 320) { 01440 return FALSE; 01441 } 01442 return (filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); 01443 } 01444 01445 /** 01446 * Checks if current e-mail sending method does not accept recipient/sender name 01447 * in a call to PHP mail() function. Windows version of mail() and mini_sendmail 01448 * program are known not to process such input correctly and they cause SMTP 01449 * errors. This function will return true if current mail sending method has 01450 * problem with recipient name in recipient/sender argument for mail(). 01451 * 01452 * TODO: 4.3 should have additional configuration variable, which is combined 01453 * by || with the rest in this function. 01454 * 01455 * @return boolean true if mail() does not accept recipient name 01456 */ 01457 public static function isBrokenEmailEnvironment() { 01458 return TYPO3_OS == 'WIN' || (FALSE !== strpos(ini_get('sendmail_path'), 'mini_sendmail')); 01459 } 01460 01461 /** 01462 * Changes from/to arguments for mail() function to work in any environment. 01463 * 01464 * @param string $address Address to adjust 01465 * @return string Adjusted address 01466 * @see t3lib_::isBrokenEmailEnvironment() 01467 */ 01468 public static function normalizeMailAddress($address) { 01469 if (self::isBrokenEmailEnvironment() && FALSE !== ($pos1 = strrpos($address, '<'))) { 01470 $pos2 = strpos($address, '>', $pos1); 01471 $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1); 01472 } 01473 return $address; 01474 } 01475 01476 /** 01477 * Formats a string for output between <textarea>-tags 01478 * All content outputted in a textarea form should be passed through this function 01479 * 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! 01480 * Usage: 23 01481 * 01482 * @param string Input string to be formatted. 01483 * @return string Formatted for <textarea>-tags 01484 */ 01485 public static function formatForTextarea($content) { 01486 return LF . htmlspecialchars($content); 01487 } 01488 01489 /** 01490 * Converts string to uppercase 01491 * The function converts all Latin characters (a-z, but no accents, etc) to 01492 * uppercase. It is safe for all supported character sets (incl. utf-8). 01493 * Unlike strtoupper() it does not honour the locale. 01494 * 01495 * @param string Input string 01496 * @return string Uppercase String 01497 */ 01498 public static function strtoupper($str) { 01499 return strtr((string) $str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); 01500 } 01501 01502 /** 01503 * Converts string to lowercase 01504 * The function converts all Latin characters (A-Z, but no accents, etc) to 01505 * lowercase. It is safe for all supported character sets (incl. utf-8). 01506 * Unlike strtolower() it does not honour the locale. 01507 * 01508 * @param string Input string 01509 * @return string Lowercase String 01510 */ 01511 public static function strtolower($str) { 01512 return strtr((string) $str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); 01513 } 01514 01515 /** 01516 * Returns a string of highly randomized bytes (over the full 8-bit range). 01517 * 01518 * @copyright Drupal CMS 01519 * @license GNU General Public License version 2 01520 * @param integer Number of characters (bytes) to return 01521 * @return string Random Bytes 01522 */ 01523 public static function generateRandomBytes($count) { 01524 $output = ''; 01525 // /dev/urandom is available on many *nix systems and is considered 01526 // the best commonly available pseudo-random source. 01527 if (TYPO3_OS != 'WIN' && ($fh = @fopen('/dev/urandom', 'rb'))) { 01528 $output = fread($fh, $count); 01529 fclose($fh); 01530 } elseif (TYPO3_OS == 'WIN') { 01531 if (class_exists('COM')) { 01532 try { 01533 $com = new COM('CAPICOM.Utilities.1'); 01534 $output = base64_decode($com->GetRandom($count, 0)); 01535 } catch (Exception $e) { 01536 // CAPICOM not installed 01537 } 01538 } 01539 if ($output === '' && version_compare(PHP_VERSION, '5.3.0', '>=')) { 01540 if (function_exists('mcrypt_create_iv')) { 01541 $output = mcrypt_create_iv($count, MCRYPT_DEV_URANDOM); 01542 } elseif (function_exists('openssl_random_pseudo_bytes')) { 01543 $isStrong = NULL; 01544 $output = openssl_random_pseudo_bytes($count, $isStrong); 01545 // skip ssl since it wasn't using the strong algo 01546 if ($isStrong !== TRUE) { 01547 $output = ''; 01548 } 01549 } 01550 } 01551 } 01552 01553 // fallback if other random byte generation failed until now 01554 if (!isset($output{$count - 1})) { 01555 // We initialize with the somewhat random. 01556 $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] 01557 . base_convert(memory_get_usage() % pow(10, 6), 10, 2) 01558 . microtime() . uniqid('') . getmypid(); 01559 while (!isset($output{$count - 1})) { 01560 $randomState = sha1(microtime() . mt_rand() . $randomState); 01561 $output .= sha1(mt_rand() . $randomState, TRUE); 01562 } 01563 $output = substr($output, strlen($output) - $count, $count); 01564 } 01565 return $output; 01566 } 01567 01568 /** 01569 * Returns a hex representation of a random byte string. 01570 * 01571 * @param integer Number of hex characters to return 01572 * @return string Random Bytes 01573 */ 01574 public static function getRandomHexString($count) { 01575 return substr(bin2hex(self::generateRandomBytes(intval(($count + 1) / 2))), 0, $count); 01576 } 01577 01578 /** 01579 * Returns a given string with underscores as UpperCamelCase. 01580 * Example: Converts blog_example to BlogExample 01581 * 01582 * @param string $string: String to be converted to camel case 01583 * @return string UpperCamelCasedWord 01584 */ 01585 public static function underscoredToUpperCamelCase($string) { 01586 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string)))); 01587 return $upperCamelCase; 01588 } 01589 01590 /** 01591 * Returns a given string with underscores as lowerCamelCase. 01592 * Example: Converts minimal_value to minimalValue 01593 * 01594 * @param string $string: String to be converted to camel case 01595 * @return string lowerCamelCasedWord 01596 */ 01597 public static function underscoredToLowerCamelCase($string) { 01598 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string)))); 01599 $lowerCamelCase = self::lcfirst($upperCamelCase); 01600 return $lowerCamelCase; 01601 } 01602 01603 /** 01604 * Returns a given CamelCasedString as an lowercase string with underscores. 01605 * Example: Converts BlogExample to blog_example, and minimalValue to minimal_value 01606 * 01607 * @param string $string: String to be converted to lowercase underscore 01608 * @return string lowercase_and_underscored_string 01609 */ 01610 public static function camelCaseToLowerCaseUnderscored($string) { 01611 return self::strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string)); 01612 } 01613 01614 /** 01615 * Converts the first char of a string to lowercase if it is a latin character (A-Z). 01616 * Example: Converts "Hello World" to "hello World" 01617 * 01618 * @param string $string: The string to be used to lowercase the first character 01619 * @return string The string with the first character as lowercase 01620 */ 01621 public static function lcfirst($string) { 01622 return self::strtolower(substr($string, 0, 1)) . substr($string, 1); 01623 } 01624 01625 /** 01626 * Checks if a given string is a Uniform Resource Locator (URL). 01627 * 01628 * @param string $url: The URL to be validated 01629 * @return boolean Whether the given URL is valid 01630 */ 01631 public static function isValidUrl($url) { 01632 return (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED) !== FALSE); 01633 } 01634 01635 01636 /************************* 01637 * 01638 * ARRAY FUNCTIONS 01639 * 01640 *************************/ 01641 01642 /** 01643 * Check if an string item exists in an array. 01644 * Please note that the order of function parameters is reverse compared to the PHP function in_array()!!! 01645 * 01646 * Comparison to PHP in_array(): 01647 * -> $array = array(0, 1, 2, 3); 01648 * -> variant_a := t3lib_div::inArray($array, $needle) 01649 * -> variant_b := in_array($needle, $array) 01650 * -> variant_c := in_array($needle, $array, true) 01651 * +---------+-----------+-----------+-----------+ 01652 * | $needle | variant_a | variant_b | variant_c | 01653 * +---------+-----------+-----------+-----------+ 01654 * | '1a' | false | true | false | 01655 * | '' | false | true | false | 01656 * | '0' | true | true | false | 01657 * | 0 | true | true | true | 01658 * +---------+-----------+-----------+-----------+ 01659 * Usage: 3 01660 * 01661 * @param array one-dimensional array of items 01662 * @param string item to check for 01663 * @return boolean true if $item is in the one-dimensional array $in_array 01664 */ 01665 public static function inArray(array $in_array, $item) { 01666 foreach ($in_array as $val) { 01667 if (!is_array($val) && !strcmp($val, $item)) { 01668 return TRUE; 01669 } 01670 } 01671 return FALSE; 01672 } 01673 01674 /** 01675 * Explodes a $string delimited by $delim and passes each item in the array through intval(). 01676 * Corresponds to t3lib_div::trimExplode(), but with conversion to integers for all values. 01677 * Usage: 76 01678 * 01679 * @param string Delimiter string to explode with 01680 * @param string The string to explode 01681 * @param boolean If set, all empty values (='') will NOT be set in output 01682 * @param integer If positive, the result will contain a maximum of limit elements, 01683 * if negative, all components except the last -limit are returned, 01684 * if zero (default), the result is not limited at all 01685 * @return array Exploded values, all converted to integers 01686 */ 01687 public static function intExplode($delimiter, $string, $onlyNonEmptyValues = FALSE, $limit = 0) { 01688 $explodedValues = self::trimExplode($delimiter, $string, $onlyNonEmptyValues, $limit); 01689 return array_map('intval', $explodedValues); 01690 } 01691 01692 /** 01693 * Reverse explode which explodes the string counting from behind. 01694 * Thus t3lib_div::revExplode(':','my:words:here',2) will return array('my:words','here') 01695 * Usage: 8 01696 * 01697 * @param string Delimiter string to explode with 01698 * @param string The string to explode 01699 * @param integer Number of array entries 01700 * @return array Exploded values 01701 */ 01702 public static function revExplode($delimiter, $string, $count = 0) { 01703 $explodedValues = explode($delimiter, strrev($string), $count); 01704 $explodedValues = array_map('strrev', $explodedValues); 01705 return array_reverse($explodedValues); 01706 } 01707 01708 /** 01709 * Explodes a string and trims all values for whitespace in the ends. 01710 * If $onlyNonEmptyValues is set, then all blank ('') values are removed. 01711 * Usage: 256 01712 * 01713 * @param string Delimiter string to explode with 01714 * @param string The string to explode 01715 * @param boolean If set, all empty values will be removed in output 01716 * @param integer If positive, the result will contain a maximum of 01717 * $limit elements, if negative, all components except 01718 * the last -$limit are returned, if zero (default), 01719 * the result is not limited at all. Attention though 01720 * that the use of this parameter can slow down this 01721 * function. 01722 * @return array Exploded values 01723 */ 01724 public static function trimExplode($delim, $string, $removeEmptyValues = FALSE, $limit = 0) { 01725 $explodedValues = explode($delim, $string); 01726 01727 $result = array_map('trim', $explodedValues); 01728 01729 if ($removeEmptyValues) { 01730 $temp = array(); 01731 foreach ($result as $value) { 01732 if ($value !== '') { 01733 $temp[] = $value; 01734 } 01735 } 01736 $result = $temp; 01737 } 01738 01739 if ($limit != 0) { 01740 if ($limit < 0) { 01741 $result = array_slice($result, 0, $limit); 01742 } elseif (count($result) > $limit) { 01743 $lastElements = array_slice($result, $limit - 1); 01744 $result = array_slice($result, 0, $limit - 1); 01745 $result[] = implode($delim, $lastElements); 01746 } 01747 } 01748 01749 return $result; 01750 } 01751 01752 /** 01753 * Removes the value $cmpValue from the $array if found there. Returns the modified array 01754 * Usage: 3 01755 * 01756 * @param array Array containing the values 01757 * @param string Value to search for and if found remove array entry where found. 01758 * @return array Output array with entries removed if search string is found 01759 */ 01760 public static function removeArrayEntryByValue(array $array, $cmpValue) { 01761 foreach ($array as $k => $v) { 01762 if (is_array($v)) { 01763 $array[$k] = self::removeArrayEntryByValue($v, $cmpValue); 01764 } elseif (!strcmp($v, $cmpValue)) { 01765 unset($array[$k]); 01766 } 01767 } 01768 return $array; 01769 } 01770 01771 /** 01772 * Filters an array to reduce its elements to match the condition. 01773 * The values in $keepItems can be optionally evaluated by a custom callback function. 01774 * 01775 * Example (arguments used to call this function): 01776 * $array = array( 01777 * array('aa' => array('first', 'second'), 01778 * array('bb' => array('third', 'fourth'), 01779 * array('cc' => array('fifth', 'sixth'), 01780 * ); 01781 * $keepItems = array('third'); 01782 * $getValueFunc = create_function('$value', 'return $value[0];'); 01783 * 01784 * Returns: 01785 * array( 01786 * array('bb' => array('third', 'fourth'), 01787 * ) 01788 * 01789 * @param array $array: The initial array to be filtered/reduced 01790 * @param mixed $keepItems: The items which are allowed/kept in the array - accepts array or csv string 01791 * @param string $getValueFunc: (optional) Unique function name set by create_function() used to get the value to keep 01792 * @return array The filtered/reduced array with the kept items 01793 */ 01794 public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = NULL) { 01795 if ($array) { 01796 // Convert strings to arrays: 01797 if (is_string($keepItems)) { 01798 $keepItems = self::trimExplode(',', $keepItems); 01799 } 01800 // create_function() returns a string: 01801 if (!is_string($getValueFunc)) { 01802 $getValueFunc = NULL; 01803 } 01804 // Do the filtering: 01805 if (is_array($keepItems) && count($keepItems)) { 01806 foreach ($array as $key => $value) { 01807 // Get the value to compare by using the callback function: 01808 $keepValue = (isset($getValueFunc) ? $getValueFunc($value) : $value); 01809 if (!in_array($keepValue, $keepItems)) { 01810 unset($array[$key]); 01811 } 01812 } 01813 } 01814 } 01815 return $array; 01816 } 01817 01818 /** 01819 * Implodes a multidim-array into GET-parameters (eg. ¶m[key][key2]=value2¶m[key][key3]=value3) 01820 * Usage: 24 01821 * 01822 * @param string Name prefix for entries. Set to blank if you wish none. 01823 * @param array The (multidim) array to implode 01824 * @param string (keep blank) 01825 * @param boolean If set, parameters which were blank strings would be removed. 01826 * @param boolean If set, the param name itself (for example "param[key][key2]") would be rawurlencoded as well. 01827 * @return string Imploded result, fx. ¶m[key][key2]=value2¶m[key][key3]=value3 01828 * @see explodeUrl2Array() 01829 */ 01830 public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = FALSE, $rawurlencodeParamName = FALSE) { 01831 foreach ($theArray as $Akey => $AVal) { 01832 $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey; 01833 if (is_array($AVal)) { 01834 $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName); 01835 } else { 01836 if (!$skipBlank || strcmp($AVal, '')) { 01837 $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) . 01838 '=' . rawurlencode($AVal); 01839 } 01840 } 01841 } 01842 return $str; 01843 } 01844 01845 /** 01846 * Explodes a string with GETvars (eg. "&id=1&type=2&ext[mykey]=3") into an array 01847 * 01848 * @param string GETvars string 01849 * @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()) 01850 * @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! 01851 * @see implodeArrayForUrl() 01852 */ 01853 public static function explodeUrl2Array($string, $multidim = FALSE) { 01854 $output = array(); 01855 if ($multidim) { 01856 parse_str($string, $output); 01857 } else { 01858 $p = explode('&', $string); 01859 foreach ($p as $v) { 01860 if (strlen($v)) { 01861 list($pK, $pV) = explode('=', $v, 2); 01862 $output[rawurldecode($pK)] = rawurldecode($pV); 01863 } 01864 } 01865 } 01866 return $output; 01867 } 01868 01869 /** 01870 * Returns an array with selected keys from incoming data. 01871 * (Better read source code if you want to find out...) 01872 * Usage: 3 01873 * 01874 * @param string List of variable/key names 01875 * @param array Array from where to get values based on the keys in $varList 01876 * @param boolean If set, then t3lib_div::_GP() is used to fetch the value if not found (isset) in the $getArray 01877 * @return array Output array with selected variables. 01878 */ 01879 public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = 1) { 01880 $keys = self::trimExplode(',', $varList, 1); 01881 $outArr = array(); 01882 foreach ($keys as $v) { 01883 if (isset($getArray[$v])) { 01884 $outArr[$v] = $getArray[$v]; 01885 } elseif ($GPvarAlt) { 01886 $outArr[$v] = self::_GP($v); 01887 } 01888 } 01889 return $outArr; 01890 } 01891 01892 /** 01893 * AddSlash array 01894 * This function traverses a multidimentional array and adds slashes to the values. 01895 * NOTE that the input array is and argument by reference.!! 01896 * Twin-function to stripSlashesOnArray 01897 * Usage: 8 01898 * 01899 * @param array Multidimensional input array, (REFERENCE!) 01900 * @return array 01901 */ 01902 public static function addSlashesOnArray(array &$theArray) { 01903 foreach ($theArray as &$value) { 01904 if (is_array($value)) { 01905 self::addSlashesOnArray($value); 01906 } else { 01907 $value = addslashes($value); 01908 } 01909 unset($value); 01910 } 01911 reset($theArray); 01912 } 01913 01914 /** 01915 * StripSlash array 01916 * This function traverses a multidimentional array and strips slashes to the values. 01917 * NOTE that the input array is and argument by reference.!! 01918 * Twin-function to addSlashesOnArray 01919 * Usage: 10 01920 * 01921 * @param array Multidimensional input array, (REFERENCE!) 01922 * @return array 01923 */ 01924 public static function stripSlashesOnArray(array &$theArray) { 01925 foreach ($theArray as &$value) { 01926 if (is_array($value)) { 01927 self::stripSlashesOnArray($value); 01928 } else { 01929 $value = stripslashes($value); 01930 } 01931 unset($value); 01932 } 01933 reset($theArray); 01934 } 01935 01936 /** 01937 * Either slashes ($cmd=add) or strips ($cmd=strip) array $arr depending on $cmd 01938 * Usage: 0 01939 * 01940 * @param array Multidimensional input array 01941 * @param string "add" or "strip", depending on usage you wish. 01942 * @return array 01943 */ 01944 public static function slashArray(array $arr, $cmd) { 01945 if ($cmd == 'strip') { 01946 self::stripSlashesOnArray($arr); 01947 } 01948 if ($cmd == 'add') { 01949 self::addSlashesOnArray($arr); 01950 } 01951 return $arr; 01952 } 01953 01954 /** 01955 * Rename Array keys with a given mapping table 01956 * @param array Array by reference which should be remapped 01957 * @param array Array with remap information, array/$oldKey => $newKey) 01958 */ 01959 function remapArrayKeys(&$array, $mappingTable) { 01960 if (is_array($mappingTable)) { 01961 foreach ($mappingTable as $old => $new) { 01962 if ($new && isset($array[$old])) { 01963 $array[$new] = $array[$old]; 01964 unset ($array[$old]); 01965 } 01966 } 01967 } 01968 } 01969 01970 01971 /** 01972 * Merges two arrays recursively and "binary safe" (integer keys are 01973 * overridden as well), overruling similar values in the first array 01974 * ($arr0) with the values of the second array ($arr1) 01975 * In case of identical keys, ie. keeping the values of the second. 01976 * Usage: 0 01977 * 01978 * @param array First array 01979 * @param array Second array, overruling the first array 01980 * @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. 01981 * @param boolean If set, values from $arr1 will overrule if they are empty or zero. Default: true 01982 * @return array Resulting array where $arr1 values has overruled $arr0 values 01983 */ 01984 public static function array_merge_recursive_overrule(array $arr0, array $arr1, $notAddKeys = 0, $includeEmtpyValues = TRUE) { 01985 foreach ($arr1 as $key => $val) { 01986 if (is_array($arr0[$key])) { 01987 if (is_array($arr1[$key])) { 01988 $arr0[$key] = self::array_merge_recursive_overrule($arr0[$key], $arr1[$key], $notAddKeys, $includeEmtpyValues); 01989 } 01990 } else { 01991 if ($notAddKeys) { 01992 if (isset($arr0[$key])) { 01993 if ($includeEmtpyValues || $val) { 01994 $arr0[$key] = $val; 01995 } 01996 } 01997 } else { 01998 if ($includeEmtpyValues || $val) { 01999 $arr0[$key] = $val; 02000 } 02001 } 02002 } 02003 } 02004 reset($arr0); 02005 return $arr0; 02006 } 02007 02008 /** 02009 * 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. 02010 * Usage: 16 02011 * 02012 * @param array First array 02013 * @param array Second array 02014 * @return array Merged result. 02015 */ 02016 public static function array_merge(array $arr1, array $arr2) { 02017 return $arr2 + $arr1; 02018 } 02019 02020 /** 02021 * Filters keys off from first array that also exist in second array. Comparision is done by keys. 02022 * This method is a recursive version of php array_diff_assoc() 02023 * 02024 * @param array Source array 02025 * @param array Reduce source array by this array 02026 * @return array Source array reduced by keys also present in second array 02027 */ 02028 public static function arrayDiffAssocRecursive(array $array1, array $array2) { 02029 $differenceArray = array(); 02030 foreach ($array1 as $key => $value) { 02031 if (!array_key_exists($key, $array2)) { 02032 $differenceArray[$key] = $value; 02033 } elseif (is_array($value)) { 02034 if (is_array($array2[$key])) { 02035 $differenceArray[$key] = self::arrayDiffAssocRecursive($value, $array2[$key]); 02036 } 02037 } 02038 } 02039 02040 return $differenceArray; 02041 } 02042 02043 /** 02044 * Takes a row and returns a CSV string of the values with $delim (default is ,) and $quote (default is ") as separator chars. 02045 * Usage: 5 02046 * 02047 * @param array Input array of values 02048 * @param string Delimited, default is comman 02049 * @param string Quote-character to wrap around the values. 02050 * @return string A single line of CSV 02051 */ 02052 public static function csvValues(array $row, $delim = ',', $quote = '"') { 02053 reset($row); 02054 $out = array(); 02055 foreach ($row as $value) { 02056 $out[] = str_replace($quote, $quote . $quote, $value); 02057 } 02058 $str = $quote . implode($quote . $delim . $quote, $out) . $quote; 02059 return $str; 02060 } 02061 02062 /** 02063 * Removes dots "." from end of a key identifier of TypoScript styled array. 02064 * array('key.' => array('property.' => 'value')) --> array('key' => array('property' => 'value')) 02065 * 02066 * @param array $ts: TypoScript configuration array 02067 * @return array TypoScript configuration array without dots at the end of all keys 02068 */ 02069 public static function removeDotsFromTS(array $ts) { 02070 $out = array(); 02071 foreach ($ts as $key => $value) { 02072 if (is_array($value)) { 02073 $key = rtrim($key, '.'); 02074 $out[$key] = self::removeDotsFromTS($value); 02075 } else { 02076 $out[$key] = $value; 02077 } 02078 } 02079 return $out; 02080 } 02081 02082 /** 02083 * Sorts an array by key recursive - uses natural sort order (aAbB-zZ) 02084 * 02085 * @param array $array array to be sorted recursively, passed by reference 02086 * @return boolean TRUE if param is an array 02087 */ 02088 public static function naturalKeySortRecursive(&$array) { 02089 if (!is_array($array)) { 02090 return FALSE; 02091 } 02092 uksort($array, 'strcasecmp'); 02093 foreach ($array as $key => $value) { 02094 self::naturalKeySortRecursive($array[$key]); 02095 } 02096 return TRUE; 02097 } 02098 02099 02100 /************************* 02101 * 02102 * HTML/XML PROCESSING 02103 * 02104 *************************/ 02105 02106 /** 02107 * Returns an array with all attributes of the input HTML tag as key/value pairs. Attributes are only lowercase a-z 02108 * $tag is either a whole tag (eg '<TAG OPTION ATTRIB=VALUE>') or the parameterlist (ex ' OPTION ATTRIB=VALUE>') 02109 * If an attribute is empty, then the value for the key is empty. You can check if it existed with isset() 02110 * Usage: 8 02111 * 02112 * @param string HTML-tag string (or attributes only) 02113 * @return array Array with the attribute values. 02114 */ 02115 public static function get_tag_attributes($tag) { 02116 $components = self::split_tag_attributes($tag); 02117 $name = ''; // attribute name is stored here 02118 $valuemode = FALSE; 02119 $attributes = array(); 02120 foreach ($components as $key => $val) { 02121 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 02122 if ($valuemode) { 02123 if ($name) { 02124 $attributes[$name] = $val; 02125 $name = ''; 02126 } 02127 } else { 02128 if ($key = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $val))) { 02129 $attributes[$key] = ''; 02130 $name = $key; 02131 } 02132 } 02133 $valuemode = FALSE; 02134 } else { 02135 $valuemode = TRUE; 02136 } 02137 } 02138 return $attributes; 02139 } 02140 02141 /** 02142 * Returns an array with the 'components' from an attribute list from an HTML tag. The result is normally analyzed by get_tag_attributes 02143 * Removes tag-name if found 02144 * Usage: 2 02145 * 02146 * @param string HTML-tag string (or attributes only) 02147 * @return array Array with the attribute values. 02148 */ 02149 public static function split_tag_attributes($tag) { 02150 $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag))); 02151 // Removes any > in the end of the string 02152 $tag_tmp = trim(rtrim($tag_tmp, '>')); 02153 02154 $value = array(); 02155 while (strcmp($tag_tmp, '')) { // Compared with empty string instead , 030102 02156 $firstChar = substr($tag_tmp, 0, 1); 02157 if (!strcmp($firstChar, '"') || !strcmp($firstChar, "'")) { 02158 $reg = explode($firstChar, $tag_tmp, 3); 02159 $value[] = $reg[1]; 02160 $tag_tmp = trim($reg[2]); 02161 } elseif (!strcmp($firstChar, '=')) { 02162 $value[] = '='; 02163 $tag_tmp = trim(substr($tag_tmp, 1)); // Removes = chars. 02164 } else { 02165 // There are '' around the value. We look for the next ' ' or '>' 02166 $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2); 02167 $value[] = trim($reg[0]); 02168 $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]); 02169 } 02170 } 02171 reset($value); 02172 return $value; 02173 } 02174 02175 /** 02176 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes) 02177 * Usage: 14 02178 * 02179 * @param array Array with attribute key/value pairs, eg. "bgcolor"=>"red", "border"=>0 02180 * @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! 02181 * @param boolean If true, don't check if values are blank. Default is to omit attributes with blank values. 02182 * @return string Imploded attributes, eg. 'bgcolor="red" border="0"' 02183 */ 02184 public static function implodeAttributes(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) { 02185 if ($xhtmlSafe) { 02186 $newArr = array(); 02187 foreach ($arr as $p => $v) { 02188 if (!isset($newArr[strtolower($p)])) { 02189 $newArr[strtolower($p)] = htmlspecialchars($v); 02190 } 02191 } 02192 $arr = $newArr; 02193 } 02194 $list = array(); 02195 foreach ($arr as $p => $v) { 02196 if (strcmp($v, '') || $dontOmitBlankAttribs) { 02197 $list[] = $p . '="' . $v . '"'; 02198 } 02199 } 02200 return implode(' ', $list); 02201 } 02202 02203 /** 02204 * Wraps JavaScript code XHTML ready with <script>-tags 02205 * Automatic re-identing of the JS code is done by using the first line as ident reference. 02206 * This is nice for identing JS code with PHP code on the same level. 02207 * 02208 * @param string JavaScript code 02209 * @param boolean Wrap script element in linebreaks? Default is TRUE. 02210 * @return string The wrapped JS code, ready to put into a XHTML page 02211 * @author Ingmar Schlecht <ingmars@web.de> 02212 * @author René Fritz <r.fritz@colorcube.de> 02213 */ 02214 public static function wrapJS($string, $linebreak = TRUE) { 02215 if (trim($string)) { 02216 // <script wrapped in nl? 02217 $cr = $linebreak ? LF : ''; 02218 02219 // remove nl from the beginning 02220 $string = preg_replace('/^\n+/', '', $string); 02221 // re-ident to one tab using the first line as reference 02222 $match = array(); 02223 if (preg_match('/^(\t+)/', $string, $match)) { 02224 $string = str_replace($match[1], TAB, $string); 02225 } 02226 $string = $cr . '<script type="text/javascript"> 02227 /*<![CDATA[*/ 02228 ' . $string . ' 02229 /*]]>*/ 02230 </script>' . $cr; 02231 } 02232 return trim($string); 02233 } 02234 02235 02236 /** 02237 * Parses XML input into a PHP array with associative keys 02238 * Usage: 0 02239 * 02240 * @param string XML data input 02241 * @param integer Number of element levels to resolve the XML into an array. Any further structure will be set as XML. 02242 * @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. 02243 * @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 02244 */ 02245 public static function xml2tree($string, $depth = 999) { 02246 $parser = xml_parser_create(); 02247 $vals = array(); 02248 $index = array(); 02249 02250 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 02251 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); 02252 xml_parse_into_struct($parser, $string, $vals, $index); 02253 02254 if (xml_get_error_code($parser)) { 02255 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser)); 02256 } 02257 xml_parser_free($parser); 02258 02259 $stack = array(array()); 02260 $stacktop = 0; 02261 $startPoint = 0; 02262 02263 $tagi = array(); 02264 foreach ($vals as $key => $val) { 02265 $type = $val['type']; 02266 02267 // open tag: 02268 if ($type == 'open' || $type == 'complete') { 02269 $stack[$stacktop++] = $tagi; 02270 02271 if ($depth == $stacktop) { 02272 $startPoint = $key; 02273 } 02274 02275 $tagi = array('tag' => $val['tag']); 02276 02277 if (isset($val['attributes'])) { 02278 $tagi['attrs'] = $val['attributes']; 02279 } 02280 if (isset($val['value'])) { 02281 $tagi['values'][] = $val['value']; 02282 } 02283 } 02284 // finish tag: 02285 if ($type == 'complete' || $type == 'close') { 02286 $oldtagi = $tagi; 02287 $tagi = $stack[--$stacktop]; 02288 $oldtag = $oldtagi['tag']; 02289 unset($oldtagi['tag']); 02290 02291 if ($depth == ($stacktop + 1)) { 02292 if ($key - $startPoint > 0) { 02293 $partArray = array_slice( 02294 $vals, 02295 $startPoint + 1, 02296 $key - $startPoint - 1 02297 ); 02298 #$oldtagi=array('XMLvalue'=>self::xmlRecompileFromStructValArray($partArray)); 02299 $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray); 02300 } else { 02301 $oldtagi['XMLvalue'] = $oldtagi['values'][0]; 02302 } 02303 } 02304 02305 $tagi['ch'][$oldtag][] = $oldtagi; 02306 unset($oldtagi); 02307 } 02308 // cdata 02309 if ($type == 'cdata') { 02310 $tagi['values'][] = $val['value']; 02311 } 02312 } 02313 return $tagi['ch']; 02314 } 02315 02316 /** 02317 * Turns PHP array into XML. See array2xml() 02318 * 02319 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though. 02320 * @param string Alternative document tag. Default is "phparray". 02321 * @param array Options for the compilation. See array2xml() for description. 02322 * @param string Forced charset to prologue 02323 * @return string An XML string made from the input content in the array. 02324 * @see xml2array(),array2xml() 02325 */ 02326 public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = array(), $charset = '') { 02327 02328 // Figure out charset if not given explicitly: 02329 if (!$charset) { 02330 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { // First priority: forceCharset! If set, this will be authoritative! 02331 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']; 02332 } elseif (is_object($GLOBALS['LANG'])) { 02333 $charset = $GLOBALS['LANG']->charSet; // If "LANG" is around, that will hold the current charset 02334 } else { 02335 $charset = 'iso-8859-1'; // THIS is just a hopeful guess! 02336 } 02337 } 02338 02339 // Return XML: 02340 return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF . 02341 self::array2xml($array, '', 0, $docTag, 0, $options); 02342 } 02343 02344 /** 02345 * Deprecated to call directly (unless you are aware of using XML prologues)! Use "array2xml_cs" instead (which adds an XML-prologue) 02346 * 02347 * Converts a PHP array into an XML string. 02348 * The XML output is optimized for readability since associative keys are used as tagnames. 02349 * 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. 02350 * Numeric keys are stored with the default tagname "numIndex" but can be overridden to other formats) 02351 * 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 02352 * 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. 02353 * 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! 02354 * 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... 02355 * Usage: 5 02356 * 02357 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though. 02358 * @param string tag-prefix, eg. a namespace prefix like "T3:" 02359 * @param integer Current recursion level. Don't change, stay at zero! 02360 * @param string Alternative document tag. Default is "phparray". 02361 * @param integer If greater than zero, then the number of spaces corresponding to this number is used for indenting, if less than zero - no indentation, if zero - a single TAB is used 02362 * @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') 02363 * @param string Stack data. Don't touch. 02364 * @return string An XML string made from the input content in the array. 02365 * @see xml2array() 02366 */ 02367 public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = array(), array $stackData = array()) { 02368 // 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 02369 $binaryChars = chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . 02370 chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) . 02371 chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) . 02372 chr(30) . chr(31); 02373 // Set indenting mode: 02374 $indentChar = $spaceInd ? ' ' : TAB; 02375 $indentN = $spaceInd > 0 ? $spaceInd : 1; 02376 $nl = ($spaceInd >= 0 ? LF : ''); 02377 02378 // Init output variable: 02379 $output = ''; 02380 02381 // Traverse the input array 02382 foreach ($array as $k => $v) { 02383 $attr = ''; 02384 $tagName = $k; 02385 02386 // Construct the tag name. 02387 if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) { // Use tag based on grand-parent + parent tag name 02388 $attr .= ' index="' . htmlspecialchars($tagName) . '"'; 02389 $tagName = (string) $options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']]; 02390 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && self::testInt($tagName)) { // Use tag based on parent tag name + if current tag is numeric 02391 $attr .= ' index="' . htmlspecialchars($tagName) . '"'; 02392 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']; 02393 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) { // Use tag based on parent tag name + current tag 02394 $attr .= ' index="' . htmlspecialchars($tagName) . '"'; 02395 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName]; 02396 } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) { // Use tag based on parent tag name: 02397 $attr .= ' index="' . htmlspecialchars($tagName) . '"'; 02398 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName']]; 02399 } elseif (!strcmp(intval($tagName), $tagName)) { // If integer...; 02400 if ($options['useNindex']) { // If numeric key, prefix "n" 02401 $tagName = 'n' . $tagName; 02402 } else { // Use special tag for num. keys: 02403 $attr .= ' index="' . $tagName . '"'; 02404 $tagName = $options['useIndexTagForNum'] ? $options['useIndexTagForNum'] : 'numIndex'; 02405 } 02406 } elseif ($options['useIndexTagForAssoc']) { // Use tag for all associative keys: 02407 $attr .= ' index="' . htmlspecialchars($tagName) . '"'; 02408 $tagName = $options['useIndexTagForAssoc']; 02409 } 02410 02411 // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either. 02412 $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100); 02413 02414 // If the value is an array then we will call this function recursively: 02415 if (is_array($v)) { 02416 02417 // Sub elements: 02418 if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) { 02419 $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName]; 02420 $clearStackPath = $subOptions['clearStackPath']; 02421 } else { 02422 $subOptions = $options; 02423 $clearStackPath = FALSE; 02424 } 02425 02426 $content = $nl . 02427 self::array2xml( 02428 $v, 02429 $NSprefix, 02430 $level + 1, 02431 '', 02432 $spaceInd, 02433 $subOptions, 02434 array( 02435 'parentTagName' => $tagName, 02436 'grandParentTagName' => $stackData['parentTagName'], 02437 'path' => $clearStackPath ? '' : $stackData['path'] . '/' . $tagName, 02438 ) 02439 ) . 02440 ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : ''); 02441 if ((int) $options['disableTypeAttrib'] != 2) { // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array 02442 $attr .= ' type="array"'; 02443 } 02444 } else { // Just a value: 02445 02446 // Look for binary chars: 02447 $vLen = strlen($v); // check for length, because PHP 5.2.0 may crash when first argument of strcspn is empty 02448 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! 02449 // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation: 02450 $content = $nl . chunk_split(base64_encode($v)); 02451 $attr .= ' base64="1"'; 02452 } else { 02453 // Otherwise, just htmlspecialchar the stuff: 02454 $content = htmlspecialchars($v); 02455 $dType = gettype($v); 02456 if ($dType == 'string') { 02457 if ($options['useCDATA'] && $content != $v) { 02458 $content = '<![CDATA[' . $v . ']]>'; 02459 } 02460 } elseif (!$options['disableTypeAttrib']) { 02461 $attr .= ' type="' . $dType . '"'; 02462 } 02463 } 02464 } 02465 02466 // Add the element to the output string: 02467 $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '') . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl; 02468 } 02469 02470 // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value: 02471 if (!$level) { 02472 $output = 02473 '<' . $docTag . '>' . $nl . 02474 $output . 02475 '</' . $docTag . '>'; 02476 } 02477 02478 return $output; 02479 } 02480 02481 /** 02482 * Converts an XML string to a PHP array. 02483 * This is the reverse function of array2xml() 02484 * This is a wrapper for xml2arrayProcess that adds a two-level cache 02485 * Usage: 17 02486 * 02487 * @param string XML content to convert into an array 02488 * @param string The tag-prefix resolve, eg. a namespace like "T3:" 02489 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array 02490 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content. 02491 * @see array2xml(),xml2arrayProcess() 02492 * @author Fabrizio Branca <typo3@fabrizio-branca.de> (added caching) 02493 */ 02494 public static function xml2array($string, $NSprefix = '', $reportDocTag = FALSE) { 02495 static $firstLevelCache = array(); 02496 02497 $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0')); 02498 02499 // look up in first level cache 02500 if (!empty($firstLevelCache[$identifier])) { 02501 $array = $firstLevelCache[$identifier]; 02502 } else { 02503 // look up in second level cache 02504 $cacheContent = t3lib_pageSelect::getHash($identifier, 0); 02505 $array = unserialize($cacheContent); 02506 02507 if ($array === FALSE) { 02508 $array = self::xml2arrayProcess($string, $NSprefix, $reportDocTag); 02509 t3lib_pageSelect::storeHash($identifier, serialize($array), 'ident_xml2array'); 02510 } 02511 // store content in first level cache 02512 $firstLevelCache[$identifier] = $array; 02513 } 02514 return $array; 02515 } 02516 02517 /** 02518 * Converts an XML string to a PHP array. 02519 * This is the reverse function of array2xml() 02520 * Usage: 1 02521 * 02522 * @param string XML content to convert into an array 02523 * @param string The tag-prefix resolve, eg. a namespace like "T3:" 02524 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array 02525 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content. 02526 * @see array2xml() 02527 */ 02528 protected function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = FALSE) { 02529 global $TYPO3_CONF_VARS; 02530 02531 // Create parser: 02532 $parser = xml_parser_create(); 02533 $vals = array(); 02534 $index = array(); 02535 02536 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); 02537 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); 02538 02539 // default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!! 02540 $match = array(); 02541 preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match); 02542 $theCharset = $match[1] ? $match[1] : ($TYPO3_CONF_VARS['BE']['forceCharset'] ? $TYPO3_CONF_VARS['BE']['forceCharset'] : 'iso-8859-1'); 02543 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1 02544 02545 // Parse content: 02546 xml_parse_into_struct($parser, $string, $vals, $index); 02547 02548 // If error, return error message: 02549 if (xml_get_error_code($parser)) { 02550 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser)); 02551 } 02552 xml_parser_free($parser); 02553 02554 // Init vars: 02555 $stack = array(array()); 02556 $stacktop = 0; 02557 $current = array(); 02558 $tagName = ''; 02559 $documentTag = ''; 02560 02561 // Traverse the parsed XML structure: 02562 foreach ($vals as $key => $val) { 02563 02564 // First, process the tag-name (which is used in both cases, whether "complete" or "close") 02565 $tagName = $val['tag']; 02566 if (!$documentTag) { 02567 $documentTag = $tagName; 02568 } 02569 02570 // Test for name space: 02571 $tagName = ($NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix) ? substr($tagName, strlen($NSprefix)) : $tagName; 02572 02573 // Test for numeric tag, encoded on the form "nXXX": 02574 $testNtag = substr($tagName, 1); // Closing tag. 02575 $tagName = (substr($tagName, 0, 1) == 'n' && !strcmp(intval($testNtag), $testNtag)) ? intval($testNtag) : $tagName; 02576 02577 // Test for alternative index value: 02578 if (strlen($val['attributes']['index'])) { 02579 $tagName = $val['attributes']['index']; 02580 } 02581 02582 // Setting tag-values, manage stack: 02583 switch ($val['type']) { 02584 case 'open': // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array: 02585 $current[$tagName] = array(); // Setting blank place holder 02586 $stack[$stacktop++] = $current; 02587 $current = array(); 02588 break; 02589 case 'close': // If the tag is "close" then it is an array which is closing and we decrease the stack pointer. 02590 $oldCurrent = $current; 02591 $current = $stack[--$stacktop]; 02592 end($current); // Going to the end of array to get placeholder key, key($current), and fill in array next: 02593 $current[key($current)] = $oldCurrent; 02594 unset($oldCurrent); 02595 break; 02596 case 'complete': // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it. 02597 if ($val['attributes']['base64']) { 02598 $current[$tagName] = base64_decode($val['value']); 02599 } else { 02600 $current[$tagName] = (string) $val['value']; // Had to cast it as a string - otherwise it would be evaluate false if tested with isset()!! 02601 02602 // Cast type: 02603 switch ((string) $val['attributes']['type']) { 02604 case 'integer': 02605 $current[$tagName] = (integer) $current[$tagName]; 02606 break; 02607 case 'double': 02608 $current[$tagName] = (double) $current[$tagName]; 02609 break; 02610 case 'boolean': 02611 $current[$tagName] = (bool) $current[$tagName]; 02612 break; 02613 case 'array': 02614 $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... 02615 break; 02616 } 02617 } 02618 break; 02619 } 02620 } 02621 02622 if ($reportDocTag) { 02623 $current[$tagName]['_DOCUMENT_TAG'] = $documentTag; 02624 } 02625 02626 // Finally return the content of the document tag. 02627 return $current[$tagName]; 02628 } 02629 02630 /** 02631 * This implodes an array of XML parts (made with xml_parse_into_struct()) into XML again. 02632 * Usage: 2 02633 * 02634 * @param array A array of XML parts, see xml2tree 02635 * @return string Re-compiled XML data. 02636 */ 02637 public static function xmlRecompileFromStructValArray(array $vals) { 02638 $XMLcontent = ''; 02639 02640 foreach ($vals as $val) { 02641 $type = $val['type']; 02642 02643 // open tag: 02644 if ($type == 'open' || $type == 'complete') { 02645 $XMLcontent .= '<' . $val['tag']; 02646 if (isset($val['attributes'])) { 02647 foreach ($val['attributes'] as $k => $v) { 02648 $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"'; 02649 } 02650 } 02651 if ($type == 'complete') { 02652 if (isset($val['value'])) { 02653 $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>'; 02654 } else { 02655 $XMLcontent .= '/>'; 02656 } 02657 } else { 02658 $XMLcontent .= '>'; 02659 } 02660 02661 if ($type == 'open' && isset($val['value'])) { 02662 $XMLcontent .= htmlspecialchars($val['value']); 02663 } 02664 } 02665 // finish tag: 02666 if ($type == 'close') { 02667 $XMLcontent .= '</' . $val['tag'] . '>'; 02668 } 02669 // cdata 02670 if ($type == 'cdata') { 02671 $XMLcontent .= htmlspecialchars($val['value']); 02672 } 02673 } 02674 02675 return $XMLcontent; 02676 } 02677 02678 /** 02679 * Extracts the attributes (typically encoding and version) of an XML prologue (header). 02680 * Usage: 1 02681 * 02682 * @param string XML data 02683 * @return array Attributes of the xml prologue (header) 02684 */ 02685 public static function xmlGetHeaderAttribs($xmlData) { 02686 $match = array(); 02687 if (preg_match('/^\s*<\?xml([^>]*)\?>/', $xmlData, $match)) { 02688 return self::get_tag_attributes($match[1]); 02689 } 02690 } 02691 02692 /** 02693 * Minifies JavaScript 02694 * 02695 * @param string $script Script to minify 02696 * @param string $error Error message (if any) 02697 * @return string Minified script or source string if error happened 02698 */ 02699 public static function minifyJavaScript($script, &$error = '') { 02700 require_once(PATH_typo3 . 'contrib/jsmin/jsmin.php'); 02701 try { 02702 $error = ''; 02703 $script = trim(JSMin::minify(str_replace(CR, '', $script))); 02704 } 02705 catch (JSMinException $e) { 02706 $error = 'Error while minifying JavaScript: ' . $e->getMessage(); 02707 self::devLog($error, 't3lib_div', 2, 02708 array('JavaScript' => $script, 'Stack trace' => $e->getTrace())); 02709 } 02710 return $script; 02711 } 02712 02713 02714 /************************* 02715 * 02716 * FILES FUNCTIONS 02717 * 02718 *************************/ 02719 02720 /** 02721 * Reads the file or url $url and returns the content 02722 * 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. 02723 * Usage: 83 02724 * 02725 * @param string File/URL to read 02726 * @param integer Whether the HTTP header should be fetched or not. 0=disable, 1=fetch header+content, 2=fetch header only 02727 * @param array HTTP headers to be used in the request 02728 * @param array Error code/message and, if $includeHeader is 1, response meta data (HTTP status and content type) 02729 * @return string The content from the resource given as input. FALSE if an error has occured. 02730 */ 02731 public static function getURL($url, $includeHeader = 0, $requestHeaders = FALSE, &$report = NULL) { 02732 $content = FALSE; 02733 02734 if (isset($report)) { 02735 $report['error'] = 0; 02736 $report['message'] = ''; 02737 } 02738 02739 // use cURL for: http, https, ftp, ftps, sftp and scp 02740 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) { 02741 if (isset($report)) { 02742 $report['lib'] = 'cURL'; 02743 } 02744 02745 // External URL without error checking. 02746 $ch = curl_init(); 02747 if (!$ch) { 02748 if (isset($report)) { 02749 $report['error'] = -1; 02750 $report['message'] = 'Couldn\'t initialize cURL.'; 02751 } 02752 return FALSE; 02753 } 02754 02755 curl_setopt($ch, CURLOPT_URL, $url); 02756 curl_setopt($ch, CURLOPT_HEADER, $includeHeader ? 1 : 0); 02757 curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0); 02758 curl_setopt($ch, CURLOPT_HTTPGET, $includeHeader == 2 ? 'HEAD' : 'GET'); 02759 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 02760 curl_setopt($ch, CURLOPT_FAILONERROR, 1); 02761 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, max(0, intval($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlTimeout']))); 02762 02763 // may fail (PHP 5.2.0+ and 5.1.5+) when open_basedir or safe_mode are enabled 02764 $followLocation = @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 02765 02766 if (is_array($requestHeaders)) { 02767 curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders); 02768 } 02769 02770 // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>) 02771 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) { 02772 curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']); 02773 02774 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) { 02775 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']); 02776 } 02777 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) { 02778 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']); 02779 } 02780 } 02781 $content = curl_exec($ch); 02782 if (isset($report)) { 02783 if ($content === FALSE) { 02784 $report['error'] = curl_errno($ch); 02785 $report['message'] = curl_error($ch); 02786 } else { 02787 $curlInfo = curl_getinfo($ch); 02788 // We hit a redirection but we couldn't follow it 02789 if (!$followLocation && $curlInfo['status'] >= 300 && $curlInfo['status'] < 400) { 02790 $report['error'] = -1; 02791 $report['message'] = 'Couldn\'t follow location redirect (either PHP configuration option safe_mode or open_basedir is in effect).'; 02792 } elseif ($includeHeader) { 02793 // Set only for $includeHeader to work exactly like PHP variant 02794 $report['http_code'] = $curlInfo['http_code']; 02795 $report['content_type'] = $curlInfo['content_type']; 02796 } 02797 } 02798 } 02799 curl_close($ch); 02800 02801 } elseif ($includeHeader) { 02802 if (isset($report)) { 02803 $report['lib'] = 'socket'; 02804 } 02805 $parsedURL = parse_url($url); 02806 if (!preg_match('/^https?/', $parsedURL['scheme'])) { 02807 if (isset($report)) { 02808 $report['error'] = -1; 02809 $report['message'] = 'Reading headers is not allowed for this protocol.'; 02810 } 02811 return FALSE; 02812 } 02813 $port = intval($parsedURL['port']); 02814 if ($port < 1) { 02815 if ($parsedURL['scheme'] == 'http') { 02816 $port = ($port > 0 ? $port : 80); 02817 $scheme = ''; 02818 } else { 02819 $port = ($port > 0 ? $port : 443); 02820 $scheme = 'ssl://'; 02821 } 02822 } 02823 $errno = 0; 02824 // $errstr = ''; 02825 $fp = @fsockopen($scheme . $parsedURL['host'], $port, $errno, $errstr, 2.0); 02826 if (!$fp || $errno > 0) { 02827 if (isset($report)) { 02828 $report['error'] = $errno ? $errno : -1; 02829 $report['message'] = $errno ? ($errstr ? $errstr : 'Socket error.') : 'Socket initialization error.'; 02830 } 02831 return FALSE; 02832 } 02833 $method = ($includeHeader == 2) ? 'HEAD' : 'GET'; 02834 $msg = $method . ' ' . $parsedURL['path'] . 02835 ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') . 02836 ' HTTP/1.0' . CRLF . 'Host: ' . 02837 $parsedURL['host'] . "\r\nConnection: close\r\n"; 02838 if (is_array($requestHeaders)) { 02839 $msg .= implode(CRLF, $requestHeaders) . CRLF; 02840 } 02841 $msg .= CRLF; 02842 02843 fputs($fp, $msg); 02844 while (!feof($fp)) { 02845 $line = fgets($fp, 2048); 02846 if (isset($report)) { 02847 if (preg_match('|^HTTP/\d\.\d +(\d+)|', $line, $status)) { 02848 $report['http_code'] = $status[1]; 02849 } 02850 elseif (preg_match('/^Content-Type: *(.*)/i', $line, $type)) { 02851 $report['content_type'] = $type[1]; 02852 } 02853 } 02854 $content .= $line; 02855 if (!strlen(trim($line))) { 02856 break; // Stop at the first empty line (= end of header) 02857 } 02858 } 02859 if ($includeHeader != 2) { 02860 $content .= stream_get_contents($fp); 02861 } 02862 fclose($fp); 02863 02864 } elseif (is_array($requestHeaders)) { 02865 if (isset($report)) { 02866 $report['lib'] = 'file/context'; 02867 } 02868 $parsedURL = parse_url($url); 02869 if (!preg_match('/^https?/', $parsedURL['scheme'])) { 02870 if (isset($report)) { 02871 $report['error'] = -1; 02872 $report['message'] = 'Sending request headers is not allowed for this protocol.'; 02873 } 02874 return FALSE; 02875 } 02876 $ctx = stream_context_create(array( 02877 'http' => array( 02878 'header' => implode(CRLF, $requestHeaders) 02879 ) 02880 ) 02881 ); 02882 $content = @file_get_contents($url, FALSE, $ctx); 02883 if ($content === FALSE && isset($report)) { 02884 $phpError = error_get_last(); 02885 $report['error'] = $phpError['type']; 02886 $report['message'] = $phpError['message']; 02887 } 02888 } else { 02889 if (isset($report)) { 02890 $report['lib'] = 'file'; 02891 } 02892 $content = @file_get_contents($url); 02893 if ($content === FALSE && isset($report)) { 02894 if (function_exists('error_get_last')) { 02895 $phpError = error_get_last(); 02896 $report['error'] = $phpError['type']; 02897 $report['message'] = $phpError['message']; 02898 } else { 02899 $report['error'] = -1; 02900 $report['message'] = 'Couldn\'t get URL.'; 02901 } 02902 } 02903 } 02904 02905 return $content; 02906 } 02907 02908 /** 02909 * Writes $content to the file $file 02910 * Usage: 30 02911 * 02912 * @param string Filepath to write to 02913 * @param string Content to write 02914 * @return boolean True if the file was successfully opened and written to. 02915 */ 02916 public static function writeFile($file, $content) { 02917 if (!@is_file($file)) { 02918 $changePermissions = TRUE; 02919 } 02920 02921 if ($fd = fopen($file, 'wb')) { 02922 $res = fwrite($fd, $content); 02923 fclose($fd); 02924 02925 if ($res === FALSE) { 02926 return FALSE; 02927 } 02928 02929 if ($changePermissions) { // Change the permissions only if the file has just been created 02930 self::fixPermissions($file); 02931 } 02932 02933 return TRUE; 02934 } 02935 02936 return FALSE; 02937 } 02938 02939 /** 02940 * Sets the file system mode and group ownership of a file or a folder. 02941 * 02942 * @param string Path of file or folder, must not be escaped. Path can be absolute or relative 02943 * @param boolean If set, also fixes permissions of files and folders in the folder (if $path is a folder) 02944 * @return mixed TRUE on success, FALSE on error, always TRUE on Windows OS 02945 */ 02946 public static function fixPermissions($path, $recursive = FALSE) { 02947 if (TYPO3_OS != 'WIN') { 02948 $result = FALSE; 02949 02950 // Make path absolute 02951 if (!self::isAbsPath($path)) { 02952 $path = self::getFileAbsFileName($path, FALSE); 02953 } 02954 02955 if (self::isAllowedAbsPath($path)) { 02956 if (@is_file($path)) { 02957 // "@" is there because file is not necessarily OWNED by the user 02958 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['fileCreateMask'])); 02959 } elseif (@is_dir($path)) { 02960 $path = preg_replace('|/$|', '', $path); 02961 // "@" is there because file is not necessarily OWNED by the user 02962 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'])); 02963 } 02964 02965 // Set createGroup if not empty 02966 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']) { 02967 // "@" is there because file is not necessarily OWNED by the user 02968 $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']); 02969 $result = $changeGroupResult ? $result : FALSE; 02970 } 02971 02972 // Call recursive if recursive flag if set and $path is directory 02973 if ($recursive && @is_dir($path)) { 02974 $handle = opendir($path); 02975 while (($file = readdir($handle)) !== FALSE) { 02976 $recursionResult = NULL; 02977 if ($file !== '.' && $file !== '..') { 02978 if (@is_file($path . '/' . $file)) { 02979 $recursionResult = self::fixPermissions($path . '/' . $file); 02980 } elseif (@is_dir($path . '/' . $file)) { 02981 $recursionResult = self::fixPermissions($path . '/' . $file, TRUE); 02982 } 02983 if (isset($recursionResult) && !$recursionResult) { 02984 $result = FALSE; 02985 } 02986 } 02987 } 02988 closedir($handle); 02989 } 02990 } 02991 } else { 02992 $result = TRUE; 02993 } 02994 return $result; 02995 } 02996 02997 /** 02998 * Writes $content to a filename in the typo3temp/ folder (and possibly one or two subfolders...) 02999 * Accepts an additional subdirectory in the file path! 03000 * 03001 * @param string Absolute filepath to write to inside "typo3temp/". First part of this string must match PATH_site."typo3temp/" 03002 * @param string Content string to write 03003 * @return string Returns NULL on success, otherwise an error string telling about the problem. 03004 */ 03005 public static function writeFileToTypo3tempDir($filepath, $content) { 03006 03007 // Parse filepath into directory and basename: 03008 $fI = pathinfo($filepath); 03009 $fI['dirname'] .= '/'; 03010 03011 // Check parts: 03012 if (self::validPathStr($filepath) && $fI['basename'] && strlen($fI['basename']) < 60) { 03013 if (defined('PATH_site')) { 03014 $dirName = PATH_site . 'typo3temp/'; // Setting main temporary directory name (standard) 03015 if (@is_dir($dirName)) { 03016 if (self::isFirstPartOfStr($fI['dirname'], $dirName)) { 03017 03018 // Checking if the "subdir" is found: 03019 $subdir = substr($fI['dirname'], strlen($dirName)); 03020 if ($subdir) { 03021 if (preg_match('/^[[:alnum:]_]+\/$/', $subdir) || preg_match('/^[[:alnum:]_]+\/[[:alnum:]_]+\/$/', $subdir)) { 03022 $dirName .= $subdir; 03023 if (!@is_dir($dirName)) { 03024 self::mkdir_deep(PATH_site . 'typo3temp/', $subdir); 03025 } 03026 } else { 03027 return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"'; 03028 } 03029 } 03030 // Checking dir-name again (sub-dir might have been created): 03031 if (@is_dir($dirName)) { 03032 if ($filepath == $dirName . $fI['basename']) { 03033 self::writeFile($filepath, $content); 03034 if (!@is_file($filepath)) { 03035 return 'File not written to disk! Write permission error in filesystem?'; 03036 } 03037 } else { 03038 return 'Calculated filelocation didn\'t match input $filepath!'; 03039 } 03040 } else { 03041 return '"' . $dirName . '" is not a directory!'; 03042 } 03043 } else { 03044 return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"'; 03045 } 03046 } else { 03047 return 'PATH_site + "typo3temp/" was not a directory!'; 03048 } 03049 } else { 03050 return 'PATH_site constant was NOT defined!'; 03051 } 03052 } else { 03053 return 'Input filepath "' . $filepath . '" was generally invalid!'; 03054 } 03055 } 03056 03057 /** 03058 * Wrapper function for mkdir. 03059 * Sets folder permissions according to $GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'] 03060 * and group ownership according to $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup'] 03061 * 03062 * @param string Absolute path to folder, see PHP mkdir() function. Removes trailing slash internally. 03063 * @return boolean TRUE if @mkdir went well! 03064 */ 03065 public static function mkdir($newFolder) { 03066 $newFolder = preg_replace('|/$|', '', $newFolder); 03067 $result = @mkdir($newFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask'])); 03068 if ($result) { 03069 self::fixPermissions($newFolder); 03070 } 03071 return $result; 03072 } 03073 03074 /** 03075 * Creates a directory - including parent directories if necessary - in the file system 03076 * 03077 * @param string Base folder. This must exist! Must have trailing slash! Example "/root/typo3site/" 03078 * @param string Deep directory to create, eg. "xx/yy/" which creates "/root/typo3site/xx/yy/" if $destination is "/root/typo3site/" 03079 * @return string If error, returns error string. 03080 */ 03081 public static function mkdir_deep($destination, $deepDir) { 03082 $allParts = self::trimExplode('/', $deepDir, 1); 03083 $root = ''; 03084 foreach ($allParts as $part) { 03085 $root .= $part . '/'; 03086 if (!is_dir($destination . $root)) { 03087 self::mkdir($destination . $root); 03088 if (!@is_dir($destination . $root)) { 03089 return 'Error: The directory "' . $destination . $root . '" could not be created...'; 03090 } 03091 } 03092 } 03093 } 03094 03095 /** 03096 * Wrapper function for rmdir, allowing recursive deletion of folders and files 03097 * 03098 * @param string Absolute path to folder, see PHP rmdir() function. Removes trailing slash internally. 03099 * @param boolean Allow deletion of non-empty directories 03100 * @return boolean true if @rmdir went well! 03101 */ 03102 public static function rmdir($path, $removeNonEmpty = FALSE) { 03103 $OK = FALSE; 03104 $path = preg_replace('|/$|', '', $path); // Remove trailing slash 03105 03106 if (file_exists($path)) { 03107 $OK = TRUE; 03108 03109 if (is_dir($path)) { 03110 if ($removeNonEmpty == TRUE && $handle = opendir($path)) { 03111 while ($OK && FALSE !== ($file = readdir($handle))) { 03112 if ($file == '.' || $file == '..') { 03113 continue; 03114 } 03115 $OK = self::rmdir($path . '/' . $file, $removeNonEmpty); 03116 } 03117 closedir($handle); 03118 } 03119 if ($OK) { 03120 $OK = rmdir($path); 03121 } 03122 03123 } else { // If $dirname is a file, simply remove it 03124 $OK = unlink($path); 03125 } 03126 03127 clearstatcache(); 03128 } 03129 03130 return $OK; 03131 } 03132 03133 /** 03134 * Returns an array with the names of folders in a specific path 03135 * Will return 'error' (string) if there were an error with reading directory content. 03136 * Usage: 11 03137 * 03138 * @param string Path to list directories from 03139 * @return array Returns an array with the directory entries as values. If no path, the return value is nothing. 03140 */ 03141 public static function get_dirs($path) { 03142 if ($path) { 03143 if (is_dir($path)) { 03144 $dir = scandir($path); 03145 $dirs = array(); 03146 foreach ($dir as $entry) { 03147 if (is_dir($path . '/' . $entry) && $entry != '..' && $entry != '.') { 03148 $dirs[] = $entry; 03149 } 03150 } 03151 } else { 03152 $dirs = 'error'; 03153 } 03154 } 03155 return $dirs; 03156 } 03157 03158 /** 03159 * Returns an array with the names of files in a specific path 03160 * Usage: 18 03161 * 03162 * @param string $path: Is the path to the file 03163 * @param string $extensionList is the comma list of extensions to read only (blank = all) 03164 * @param boolean If set, then the path is prepended the filenames. Otherwise only the filenames are returned in the array 03165 * @param string $order is sorting: 1= sort alphabetically, 'mtime' = sort by modification time. 03166 * @param string A comma seperated list of filenames to exclude, no wildcards 03167 * @return array Array of the files found 03168 */ 03169 public static function getFilesInDir($path, $extensionList = '', $prependPath = 0, $order = '', $excludePattern = '') { 03170 03171 // Initialize variabels: 03172 $filearray = array(); 03173 $sortarray = array(); 03174 $path = rtrim($path, '/'); 03175 03176 // Find files+directories: 03177 if (@is_dir($path)) { 03178 $extensionList = strtolower($extensionList); 03179 $d = dir($path); 03180 if (is_object($d)) { 03181 while ($entry = $d->read()) { 03182 if (@is_file($path . '/' . $entry)) { 03183 $fI = pathinfo($entry); 03184 $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) 03185 if ((!strlen($extensionList) || self::inList($extensionList, strtolower($fI['extension']))) && (!strlen($excludePattern) || !preg_match('/^' . $excludePattern . '$/', $entry))) { 03186 $filearray[$key] = ($prependPath ? $path . '/' : '') . $entry; 03187 if ($order == 'mtime') { 03188 $sortarray[$key] = filemtime($path . '/' . $entry); 03189 } 03190 elseif ($order) { 03191 $sortarray[$key] = $entry; 03192 } 03193 } 03194 } 03195 } 03196 $d->close(); 03197 } else { 03198 return 'error opening path: "' . $path . '"'; 03199 } 03200 } 03201 03202 // Sort them: 03203 if ($order) { 03204 asort($sortarray); 03205 $newArr = array(); 03206 foreach ($sortarray as $k => $v) { 03207 $newArr[$k] = $filearray[$k]; 03208 } 03209 $filearray = $newArr; 03210 } 03211 03212 // Return result 03213 reset($filearray); 03214 return $filearray; 03215 } 03216 03217 /** 03218 * Recursively gather all files and folders of a path. 03219 * Usage: 5 03220 * 03221 * @param array $fileArr: Empty input array (will have files added to it) 03222 * @param string $path: The path to read recursively from (absolute) (include trailing slash!) 03223 * @param string $extList: Comma list of file extensions: Only files with extensions in this list (if applicable) will be selected. 03224 * @param boolean $regDirs: If set, directories are also included in output. 03225 * @param integer $recursivityLevels: The number of levels to dig down... 03226 * @param string $excludePattern: regex pattern of files/directories to exclude 03227 * @return array An array with the found files/directories. 03228 */ 03229 public static function getAllFilesAndFoldersInPath(array $fileArr, $path, $extList = '', $regDirs = 0, $recursivityLevels = 99, $excludePattern = '') { 03230 if ($regDirs) { 03231 $fileArr[] = $path; 03232 } 03233 $fileArr = array_merge($fileArr, self::getFilesInDir($path, $extList, 1, 1, $excludePattern)); 03234 03235 $dirs = self::get_dirs($path); 03236 if (is_array($dirs) && $recursivityLevels > 0) { 03237 foreach ($dirs as $subdirs) { 03238 if ((string) $subdirs != '' && (!strlen($excludePattern) || !preg_match('/^' . $excludePattern . '$/', $subdirs))) { 03239 $fileArr = self::getAllFilesAndFoldersInPath($fileArr, $path . $subdirs . '/', $extList, $regDirs, $recursivityLevels - 1, $excludePattern); 03240 } 03241 } 03242 } 03243 return $fileArr; 03244 } 03245 03246 /** 03247 * Removes the absolute part of all files/folders in fileArr 03248 * Usage: 2 03249 * 03250 * @param array $fileArr: The file array to remove the prefix from 03251 * @param string $prefixToRemove: The prefix path to remove (if found as first part of string!) 03252 * @return array The input $fileArr processed. 03253 */ 03254 public static function removePrefixPathFromList(array $fileArr, $prefixToRemove) { 03255 foreach ($fileArr as $k => &$absFileRef) { 03256 if (self::isFirstPartOfStr($absFileRef, $prefixToRemove)) { 03257 $absFileRef = substr($absFileRef, strlen($prefixToRemove)); 03258 } else { 03259 return 'ERROR: One or more of the files was NOT prefixed with the prefix-path!'; 03260 } 03261 } 03262 return $fileArr; 03263 } 03264 03265 /** 03266 * Fixes a path for windows-backslashes and reduces double-slashes to single slashes 03267 * Usage: 2 03268 * 03269 * @param string File path to process 03270 * @return string 03271 */ 03272 public static function fixWindowsFilePath($theFile) { 03273 return str_replace('//', '/', str_replace('\\', '/', $theFile)); 03274 } 03275 03276 /** 03277 * Resolves "../" sections in the input path string. 03278 * For example "fileadmin/directory/../other_directory/" will be resolved to "fileadmin/other_directory/" 03279 * Usage: 2 03280 * 03281 * @param string File path in which "/../" is resolved 03282 * @return string 03283 */ 03284 public static function resolveBackPath($pathStr) { 03285 $parts = explode('/', $pathStr); 03286 $output = array(); 03287 $c = 0; 03288 foreach ($parts as $pV) { 03289 if ($pV == '..') { 03290 if ($c) { 03291 array_pop($output); 03292 $c--; 03293 } else { 03294 $output[] = $pV; 03295 } 03296 } else { 03297 $c++; 03298 $output[] = $pV; 03299 } 03300 } 03301 return implode('/', $output); 03302 } 03303 03304 /** 03305 * Prefixes a URL used with 'header-location' with 'http://...' depending on whether it has it already. 03306 * - If already having a scheme, nothing is prepended 03307 * - If having REQUEST_URI slash '/', then prefixing 'http://[host]' (relative to host) 03308 * - Otherwise prefixed with TYPO3_REQUEST_DIR (relative to current dir / TYPO3_REQUEST_DIR) 03309 * Usage: 30 03310 * 03311 * @param string URL / path to prepend full URL addressing to. 03312 * @return string 03313 */ 03314 public static function locationHeaderUrl($path) { 03315 $uI = parse_url($path); 03316 if (substr($path, 0, 1) == '/') { // relative to HOST 03317 $path = self::getIndpEnv('TYPO3_REQUEST_HOST') . $path; 03318 } elseif (!$uI['scheme']) { // No scheme either 03319 $path = self::getIndpEnv('TYPO3_REQUEST_DIR') . $path; 03320 } 03321 return $path; 03322 } 03323 03324 /** 03325 * Returns the maximum upload size for a file that is allowed. Measured in KB. 03326 * This might be handy to find out the real upload limit that is possible for this 03327 * TYPO3 installation. The first parameter can be used to set something that overrides 03328 * the maxFileSize, usually for the TCA values. 03329 * 03330 * @param integer $localLimit: the number of Kilobytes (!) that should be used as 03331 * the initial Limit, otherwise $TYPO3_CONF_VARS['BE']['maxFileSize'] will be used 03332 * @return integer the maximum size of uploads that are allowed (measuered in kilobytes) 03333 */ 03334 public static function getMaxUploadFileSize($localLimit = 0) { 03335 // don't allow more than the global max file size at all 03336 $t3Limit = (intval($localLimit > 0 ? $localLimit : $GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize'])); 03337 // as TYPO3 is handling the file size in KB, multiply by 1024 to get bytes 03338 $t3Limit = $t3Limit * 1024; 03339 03340 // check for PHP restrictions of the maximum size of one of the $_FILES 03341 $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize')); 03342 // check for PHP restrictions of the maximum $_POST size 03343 $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size')); 03344 // if the total amount of post data is smaller (!) than the upload_max_filesize directive, 03345 // then this is the real limit in PHP 03346 $phpUploadLimit = ($phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit); 03347 03348 // is the allowed PHP limit (upload_max_filesize) lower than the TYPO3 limit?, also: revert back to KB 03349 return floor($phpUploadLimit < $t3Limit ? $phpUploadLimit : $t3Limit) / 1024; 03350 } 03351 03352 /** 03353 * Gets the bytes value from a measurement string like "100k". 03354 * 03355 * @param string $measurement: The measurement (e.g. "100k") 03356 * @return integer The bytes value (e.g. 102400) 03357 */ 03358 public static function getBytesFromSizeMeasurement($measurement) { 03359 if (stripos($measurement, 'G')) { 03360 $bytes = doubleval($measurement) * 1024 * 1024 * 1024; 03361 } else { 03362 if (stripos($measurement, 'M')) { 03363 $bytes = doubleval($measurement) * 1024 * 1024; 03364 } else { 03365 if (stripos($measurement, 'K')) { 03366 $bytes = doubleval($measurement) * 1024; 03367 } else { 03368 $bytes = doubleval($measurement); 03369 } 03370 } 03371 } 03372 return $bytes; 03373 } 03374 03375 /** 03376 * Retrieves the maximum path length that is valid in the current environment. 03377 * 03378 * @return integer The maximum available path length 03379 * @author Ingo Renner <ingo@typo3.org> 03380 */ 03381 public static function getMaximumPathLength() { 03382 $maximumPathLength = 0; 03383 03384 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 03385 // rough assumptions 03386 if (TYPO3_OS == 'WIN') { 03387 // WIN is usually 255, Vista 260, although NTFS can hold about 2k 03388 $maximumPathLength = 255; 03389 } else { 03390 $maximumPathLength = 2048; 03391 } 03392 } else { 03393 // precise information is available since PHP 5.3 03394 $maximumPathLength = PHP_MAXPATHLEN; 03395 } 03396 03397 return $maximumPathLength; 03398 } 03399 03400 03401 /** 03402 * Function for static version numbers on files, based on the filemtime 03403 * 03404 * This will make the filename automatically change when a file is 03405 * changed, and by that re-cached by the browser. If the file does not 03406 * exist physically the original file passed to the function is 03407 * returned without the timestamp. 03408 * 03409 * Behaviour is influenced by the setting 03410 * TYPO3_CONF_VARS[TYPO3_MODE][versionNumberInFilename] 03411 * = true (BE) / "embed" (FE) : modify filename 03412 * = false (BE) / "querystring" (FE) : add timestamp as parameter 03413 * 03414 * @param string $file Relative path to file including all potential query parameters (not htmlspecialchared yet) 03415 * @param boolean $forceQueryString If settings would suggest to embed in filename, this parameter allows us to force the versioning to occur in the query string. This is needed for scriptaculous.js which cannot have a different filename in order to load its modules (?load=...) 03416 * @return Relative path with version filename including the timestamp 03417 * @author Lars Houmark <lars@houmark.com> 03418 */ 03419 public static function createVersionNumberedFilename($file, $forceQueryString = FALSE) { 03420 $lookupFile = explode('?', $file); 03421 $path = self::resolveBackPath(self::dirname(PATH_thisScript) . '/' . $lookupFile[0]); 03422 03423 if (TYPO3_MODE == 'FE') { 03424 $mode = strtolower($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']); 03425 if ($mode === 'embed') { 03426 $mode = TRUE; 03427 } else { 03428 if ($mode === 'querystring') { 03429 $mode = FALSE; 03430 } else { 03431 $doNothing = TRUE; 03432 } 03433 } 03434 } else { 03435 $mode = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']; 03436 } 03437 03438 if (!file_exists($path) || $doNothing) { 03439 // File not found, return filename unaltered 03440 $fullName = $file; 03441 03442 } else { 03443 if (!$mode || $forceQueryString) { 03444 // If use of .htaccess rule is not configured, 03445 // we use the default query-string method 03446 if ($lookupFile[1]) { 03447 $separator = '&'; 03448 } else { 03449 $separator = '?'; 03450 } 03451 $fullName = $file . $separator . filemtime($path); 03452 03453 } else { 03454 // Change the filename 03455 $name = explode('.', $lookupFile[0]); 03456 $extension = array_pop($name); 03457 03458 array_push($name, filemtime($path), $extension); 03459 $fullName = implode('.', $name); 03460 // append potential query string 03461 $fullName .= $lookupFile[1] ? '?' . $lookupFile[1] : ''; 03462 } 03463 } 03464 03465 return $fullName; 03466 } 03467 03468 03469 /************************* 03470 * 03471 * DEBUG helper FUNCTIONS 03472 * 03473 *************************/ 03474 03475 /* Deprecated since 4.5, use t3lib_utility_Debug */ 03476 03477 03478 /** 03479 * Returns a string with a list of ascii-values for the first $characters characters in $string 03480 * Usage: 0 03481 * 03482 * @param string String to show ASCII value for 03483 * @param integer Number of characters to show 03484 * @return string The string with ASCII values in separated by a space char. 03485 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::ordinalValue instead 03486 */ 03487 public static function debug_ordvalue($string, $characters = 100) { 03488 self::logDeprecatedFunction(); 03489 return t3lib_utility_Debug::ordinalValue($string, $characters); 03490 } 03491 03492 /** 03493 * Returns HTML-code, which is a visual representation of a multidimensional array 03494 * use t3lib_div::print_array() in order to print an array 03495 * Returns false if $array_in is not an array 03496 * Usage: 31 03497 * 03498 * @param mixed Array to view 03499 * @return string HTML output 03500 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::viewArray instead 03501 */ 03502 public static function view_array($array_in) { 03503 self::logDeprecatedFunction(); 03504 return t3lib_utility_Debug::viewArray($array_in); 03505 } 03506 03507 /** 03508 * Prints an array 03509 * Usage: 6 03510 * 03511 * @param mixed Array to print visually (in a table). 03512 * @return void 03513 * @see view_array() 03514 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::printArray instead 03515 */ 03516 public static function print_array($array_in) { 03517 self::logDeprecatedFunction(); 03518 t3lib_utility_Debug::printArray($array_in); 03519 } 03520 03521 /** 03522 * Makes debug output 03523 * Prints $var in bold between two vertical lines 03524 * If not $var the word 'debug' is printed 03525 * If $var is an array, the array is printed by t3lib_div::print_array() 03526 * Usage: 8 03527 * 03528 * @param mixed Variable to print 03529 * @param string The header. 03530 * @param string Group for the debug console 03531 * @return void 03532 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debug instead 03533 */ 03534 public static function debug($var = '', $header = '', $group = 'Debug') { 03535 self::logDeprecatedFunction(); 03536 t3lib_utility_Debug::debug($var, $header, $group); 03537 } 03538 03539 /** 03540 * Displays the "path" of the function call stack in a string, using debug_backtrace 03541 * 03542 * @return string 03543 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debugTrail instead 03544 */ 03545 public static function debug_trail() { 03546 self::logDeprecatedFunction(); 03547 return t3lib_utility_Debug::debugTrail(); 03548 } 03549 03550 /** 03551 * Displays an array as rows in a table. Useful to debug output like an array of database records. 03552 * 03553 * @param mixed Array of arrays with similar keys 03554 * @param string Table header 03555 * @param boolean If TRUE, will return content instead of echo'ing out. 03556 * @return void Outputs to browser. 03557 * @deprecated since TYPO3 4.5 - Use t3lib_utility_Debug::debugRows instead 03558 */ 03559 public static function debugRows($rows, $header = '', $returnHTML = FALSE) { 03560 self::logDeprecatedFunction(); 03561 t3lib_utility_Debug::debugRows($rows, $header, $returnHTML); 03562 } 03563 03564 03565 /************************* 03566 * 03567 * SYSTEM INFORMATION 03568 * 03569 *************************/ 03570 03571 /** 03572 * Returns the HOST+DIR-PATH of the current script (The URL, but without 'http://' and without script-filename) 03573 * Usage: 1 03574 * 03575 * @return string 03576 */ 03577 public static function getThisUrl() { 03578 $p = parse_url(self::getIndpEnv('TYPO3_REQUEST_SCRIPT')); // Url of this script 03579 $dir = self::dirname($p['path']) . '/'; // Strip file 03580 $url = str_replace('//', '/', $p['host'] . ($p['port'] ? ':' . $p['port'] : '') . $dir); 03581 return $url; 03582 } 03583 03584 /** 03585 * Returns the link-url to the current script. 03586 * 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. 03587 * REMEMBER to always use htmlspecialchars() for content in href-properties to get ampersands converted to entities (XHTML requirement and XSS precaution) 03588 * Usage: 52 03589 * 03590 * @param array Array of GET parameters to include 03591 * @return string 03592 */ 03593 public static function linkThisScript(array $getParams = array()) { 03594 $parts = self::getIndpEnv('SCRIPT_NAME'); 03595 $params = self::_GET(); 03596 03597 foreach ($getParams as $key => $value) { 03598 if ($value !== '') { 03599 $params[$key] = $value; 03600 } else { 03601 unset($params[$key]); 03602 } 03603 } 03604 03605 $pString = self::implodeArrayForUrl('', $params); 03606 03607 return $pString ? $parts . '?' . preg_replace('/^&/', '', $pString) : $parts; 03608 } 03609 03610 /** 03611 * 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. 03612 * So basically it adds the parameters in $getParams to an existing URL, $url 03613 * Usage: 2 03614 * 03615 * @param string URL string 03616 * @param array Array of key/value pairs for get parameters to add/overrule with. Can be multidimensional. 03617 * @return string Output URL with added getParams. 03618 */ 03619 public static function linkThisUrl($url, array $getParams = array()) { 03620 $parts = parse_url($url); 03621 $getP = array(); 03622 if ($parts['query']) { 03623 parse_str($parts['query'], $getP); 03624 } 03625 $getP = self::array_merge_recursive_overrule($getP, $getParams); 03626 $uP = explode('?', $url); 03627 03628 $params = self::implodeArrayForUrl('', $getP); 03629 $outurl = $uP[0] . ($params ? '?' . substr($params, 1) : ''); 03630 03631 return $outurl; 03632 } 03633 03634 /** 03635 * Abstraction method which returns System Environment Variables regardless of server OS, CGI/MODULE version etc. Basically this is SERVER variables for most of them. 03636 * This should be used instead of getEnv() and $_SERVER/ENV_VARS to get reliable values for all situations. 03637 * Usage: 221 03638 * 03639 * @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 03640 * @return string Value based on the input key, independent of server/os environment. 03641 */ 03642 public static function getIndpEnv($getEnvName) { 03643 /* 03644 Conventions: 03645 output from parse_url(): 03646 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 03647 [scheme] => 'http' 03648 [user] => 'username' 03649 [pass] => 'password' 03650 [host] => '192.168.1.4' 03651 [port] => '8080' 03652 [path] => '/typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/' 03653 [query] => 'arg1,arg2,arg3&p1=parameter1&p2[key]=value' 03654 [fragment] => 'link1' 03655 03656 Further definition: [path_script] = '/typo3/32/temp/phpcheck/index.php' 03657 [path_dir] = '/typo3/32/temp/phpcheck/' 03658 [path_info] = '/arg1/arg2/arg3/' 03659 [path] = [path_script/path_dir][path_info] 03660 03661 03662 Keys supported: 03663 03664 URI______: 03665 REQUEST_URI = [path]?[query] = /typo3/32/temp/phpcheck/index.php/arg1/arg2/arg3/?arg1,arg2,arg3&p1=parameter1&p2[key]=value 03666 HTTP_HOST = [host][:[port]] = 192.168.1.4:8080 03667 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')! 03668 PATH_INFO = [path_info] = /arg1/arg2/arg3/ 03669 QUERY_STRING = [query] = arg1,arg2,arg3&p1=parameter1&p2[key]=value 03670 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 03671 (Notice: NO username/password + NO fragment) 03672 03673 CLIENT____: 03674 REMOTE_ADDR = (client IP) 03675 REMOTE_HOST = (client host) 03676 HTTP_USER_AGENT = (client user agent) 03677 HTTP_ACCEPT_LANGUAGE = (client accept language) 03678 03679 SERVER____: 03680 SCRIPT_FILENAME = Absolute filename of script (Differs between windows/unix). On windows 'C:\\blabla\\blabl\\' will be converted to 'C:/blabla/blabl/' 03681 03682 Special extras: 03683 TYPO3_HOST_ONLY = [host] = 192.168.1.4 03684 TYPO3_PORT = [port] = 8080 (blank if 80, taken from host value) 03685 TYPO3_REQUEST_HOST = [scheme]://[host][:[port]] 03686 TYPO3_REQUEST_URL = [scheme]://[host][:[port]][path]?[query] (scheme will by default be "http" until we can detect something different) 03687 TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script] 03688 TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir] 03689 TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend 03690 TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend 03691 TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website 03692 TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically) 03693 TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https) 03694 TYPO3_PROXY = Returns TRUE if this session runs over a well known proxy 03695 03696 Notice: [fragment] is apparently NEVER available to the script! 03697 03698 03699 Testing suggestions: 03700 - Output all the values. 03701 - 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 03702 - ALSO TRY the script from the ROOT of a site (like 'http://www.mytest.com/' and not 'http://www.mytest.com/test/' !!) 03703 03704 */ 03705 03706 # if ($getEnvName=='HTTP_REFERER') return ''; 03707 03708 $retVal = ''; 03709 03710 switch ((string) $getEnvName) { 03711 case 'SCRIPT_NAME': 03712 $retVal = (PHP_SAPI == 'fpm-fcgi' || PHP_SAPI == 'cgi' || PHP_SAPI == 'cgi-fcgi') && 03713 ($_SERVER['ORIG_PATH_INFO'] ? $_SERVER['ORIG_PATH_INFO'] : $_SERVER['PATH_INFO']) ? 03714 ($_SERVER['ORIG_PATH_INFO'] ? $_SERVER['ORIG_PATH_INFO'] : $_SERVER['PATH_INFO']) : 03715 ($_SERVER['ORIG_SCRIPT_NAME'] ? $_SERVER['ORIG_SCRIPT_NAME'] : $_SERVER['SCRIPT_NAME']); 03716 // add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix 03717 if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) { 03718 if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) { 03719 $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal; 03720 } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) { 03721 $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal; 03722 } 03723 } 03724 break; 03725 case 'SCRIPT_FILENAME': 03726 $retVal = str_replace('//', '/', str_replace('\\', '/', 03727 (PHP_SAPI == 'fpm-fcgi' || PHP_SAPI == 'cgi' || PHP_SAPI == 'isapi' || PHP_SAPI == 'cgi-fcgi') && 03728 ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) ? 03729 ($_SERVER['ORIG_PATH_TRANSLATED'] ? $_SERVER['ORIG_PATH_TRANSLATED'] : $_SERVER['PATH_TRANSLATED']) : 03730 ($_SERVER['ORIG_SCRIPT_FILENAME'] ? $_SERVER['ORIG_SCRIPT_FILENAME'] : $_SERVER['SCRIPT_FILENAME']))); 03731 03732 break; 03733 case 'REQUEST_URI': 03734 // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI')) 03735 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) 03736 list($v, $n) = explode('|', $GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']); 03737 $retVal = $GLOBALS[$v][$n]; 03738 } elseif (!$_SERVER['REQUEST_URI']) { // This is for ISS/CGI which does not have the REQUEST_URI available. 03739 $retVal = '/' . ltrim(self::getIndpEnv('SCRIPT_NAME'), '/') . 03740 ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : ''); 03741 } else { 03742 $retVal = $_SERVER['REQUEST_URI']; 03743 } 03744 // add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix 03745 if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) { 03746 if (self::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) { 03747 $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL'] . $retVal; 03748 } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix']) { 03749 $retVal = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefix'] . $retVal; 03750 } 03751 } 03752 break; 03753 case 'PATH_INFO': 03754 // $_SERVER['PATH_INFO']!=$_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI) are seen to set PATH_INFO equal to script_name 03755 // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense. 03756 // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers, then 'PHP_SAPI=='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=='cgi' (see above) 03757 // if (strcmp($_SERVER['PATH_INFO'],self::getIndpEnv('SCRIPT_NAME')) && count(explode('/',$_SERVER['PATH_INFO']))>1) { 03758 if (PHP_SAPI != 'cgi' && PHP_SAPI != 'cgi-fcgi' && PHP_SAPI != 'fpm-fcgi') { 03759 $retVal = $_SERVER['PATH_INFO']; 03760 } 03761 break; 03762 case 'TYPO3_REV_PROXY': 03763 $retVal = self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']); 03764 break; 03765 case 'REMOTE_ADDR': 03766 $retVal = $_SERVER['REMOTE_ADDR']; 03767 if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) { 03768 $ip = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); 03769 // choose which IP in list to use 03770 if (count($ip)) { 03771 switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) { 03772 case 'last': 03773 $ip = array_pop($ip); 03774 break; 03775 case 'first': 03776 $ip = array_shift($ip); 03777 break; 03778 case 'none': 03779 default: 03780 $ip = ''; 03781 break; 03782 } 03783 } 03784 if (self::validIP($ip)) { 03785 $retVal = $ip; 03786 } 03787 } 03788 break; 03789 case 'HTTP_HOST': 03790 $retVal = $_SERVER['HTTP_HOST']; 03791 if (self::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) { 03792 $host = self::trimExplode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); 03793 // choose which host in list to use 03794 if (count($host)) { 03795 switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyHeaderMultiValue']) { 03796 case 'last': 03797 $host = array_pop($host); 03798 break; 03799 case 'first': 03800 $host = array_shift($host); 03801 break; 03802 case 'none': 03803 default: 03804 $host = ''; 03805 break; 03806 } 03807 } 03808 if ($host) { 03809 $retVal = $host; 03810 } 03811 } 03812 break; 03813 // These are let through without modification 03814 case 'HTTP_REFERER': 03815 case 'HTTP_USER_AGENT': 03816 case 'HTTP_ACCEPT_ENCODING': 03817 case 'HTTP_ACCEPT_LANGUAGE': 03818 case 'REMOTE_HOST': 03819 case 'QUERY_STRING': 03820 $retVal = $_SERVER[$getEnvName]; 03821 break; 03822 case 'TYPO3_DOCUMENT_ROOT': 03823 // Get the web root (it is not the root of the TYPO3 installation) 03824 // The absolute path of the script can be calculated with TYPO3_DOCUMENT_ROOT + SCRIPT_FILENAME 03825 // 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. 03826 // Therefore the DOCUMENT_ROOT is now always calculated as the SCRIPT_FILENAME minus the end part shared with SCRIPT_NAME. 03827 $SFN = self::getIndpEnv('SCRIPT_FILENAME'); 03828 $SN_A = explode('/', strrev(self::getIndpEnv('SCRIPT_NAME'))); 03829 $SFN_A = explode('/', strrev($SFN)); 03830 $acc = array(); 03831 foreach ($SN_A as $kk => $vv) { 03832 if (!strcmp($SFN_A[$kk], $vv)) { 03833 $acc[] = $vv; 03834 } else { 03835 break; 03836 } 03837 } 03838 $commonEnd = strrev(implode('/', $acc)); 03839 if (strcmp($commonEnd, '')) { 03840 $DR = substr($SFN, 0, -(strlen($commonEnd) + 1)); 03841 } 03842 $retVal = $DR; 03843 break; 03844 case 'TYPO3_HOST_ONLY': 03845 $httpHost = self::getIndpEnv('HTTP_HOST'); 03846 $httpHostBracketPosition = strpos($httpHost, ']'); 03847 $retVal = ($httpHostBracketPosition !== FALSE) ? substr($httpHost, 0, ($httpHostBracketPosition + 1)) : array_shift(explode(':', $httpHost)); 03848 break; 03849 case 'TYPO3_PORT': 03850 $httpHost = self::getIndpEnv('HTTP_HOST'); 03851 $httpHostOnly = self::getIndpEnv('TYPO3_HOST_ONLY'); 03852 $retVal = (strlen($httpHost) > strlen($httpHostOnly)) ? substr($httpHost, strlen($httpHostOnly) + 1) : ''; 03853 break; 03854 case 'TYPO3_REQUEST_HOST': 03855 $retVal = (self::getIndpEnv('TYPO3_SSL') ? 'https://' : 'http://') . 03856 self::getIndpEnv('HTTP_HOST'); 03857 break; 03858 case 'TYPO3_REQUEST_URL': 03859 $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('REQUEST_URI'); 03860 break; 03861 case 'TYPO3_REQUEST_SCRIPT': 03862 $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::getIndpEnv('SCRIPT_NAME'); 03863 break; 03864 case 'TYPO3_REQUEST_DIR': 03865 $retVal = self::getIndpEnv('TYPO3_REQUEST_HOST') . self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/'; 03866 break; 03867 case 'TYPO3_SITE_URL': 03868 if (defined('PATH_thisScript') && defined('PATH_site')) { 03869 $lPath = substr(dirname(PATH_thisScript), strlen(PATH_site)) . '/'; 03870 $url = self::getIndpEnv('TYPO3_REQUEST_DIR'); 03871 $siteUrl = substr($url, 0, -strlen($lPath)); 03872 if (substr($siteUrl, -1) != '/') { 03873 $siteUrl .= '/'; 03874 } 03875 $retVal = $siteUrl; 03876 } 03877 break; 03878 case 'TYPO3_SITE_PATH': 03879 $retVal = substr(self::getIndpEnv('TYPO3_SITE_URL'), strlen(self::getIndpEnv('TYPO3_REQUEST_HOST'))); 03880 break; 03881 case 'TYPO3_SITE_SCRIPT': 03882 $retVal = substr(self::getIndpEnv('TYPO3_REQUEST_URL'), strlen(self::getIndpEnv('TYPO3_SITE_URL'))); 03883 break; 03884 case 'TYPO3_SSL': 03885 $proxySSL = trim($GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL']); 03886 if ($proxySSL == '*') { 03887 $proxySSL = $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP']; 03888 } 03889 if (self::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) { 03890 $retVal = TRUE; 03891 } else { 03892 $retVal = $_SERVER['SSL_SESSION_ID'] || !strcasecmp($_SERVER['HTTPS'], 'on') || !strcmp($_SERVER['HTTPS'], '1') ? TRUE : FALSE; // see http://bugs.typo3.org/view.php?id=3909 03893 } 03894 break; 03895 case '_ARRAY': 03896 $out = array(); 03897 // Here, list ALL possible keys to this function for debug display. 03898 $envTestVars = self::trimExplode(',', ' 03899 HTTP_HOST, 03900 TYPO3_HOST_ONLY, 03901 TYPO3_PORT, 03902 PATH_INFO, 03903 QUERY_STRING, 03904 REQUEST_URI, 03905 HTTP_REFERER, 03906 TYPO3_REQUEST_HOST, 03907 TYPO3_REQUEST_URL, 03908 TYPO3_REQUEST_SCRIPT, 03909 TYPO3_REQUEST_DIR, 03910 TYPO3_SITE_URL, 03911 TYPO3_SITE_SCRIPT, 03912 TYPO3_SSL, 03913 TYPO3_REV_PROXY, 03914 SCRIPT_NAME, 03915 TYPO3_DOCUMENT_ROOT, 03916 SCRIPT_FILENAME, 03917 REMOTE_ADDR, 03918 REMOTE_HOST, 03919 HTTP_USER_AGENT, 03920 HTTP_ACCEPT_LANGUAGE', 1); 03921 foreach ($envTestVars as $v) { 03922 $out[$v] = self::getIndpEnv($v); 03923 } 03924 reset($out); 03925 $retVal = $out; 03926 break; 03927 } 03928 return $retVal; 03929 } 03930 03931 /** 03932 * Gets the unixtime as milliseconds. 03933 * 03934 * @return integer The unixtime as milliseconds 03935 */ 03936 public static function milliseconds() { 03937 return round(microtime(TRUE) * 1000); 03938 } 03939 03940 /** 03941 * Client Browser Information 03942 * Usage: 4 03943 * 03944 * @param string Alternative User Agent string (if empty, t3lib_div::getIndpEnv('HTTP_USER_AGENT') is used) 03945 * @return array Parsed information about the HTTP_USER_AGENT in categories BROWSER, VERSION, SYSTEM and FORMSTYLE 03946 */ 03947 public static function clientInfo($useragent = '') { 03948 if (!$useragent) { 03949 $useragent = self::getIndpEnv('HTTP_USER_AGENT'); 03950 } 03951 03952 $bInfo = array(); 03953 // Which browser? 03954 if (strpos($useragent, 'Konqueror') !== FALSE) { 03955 $bInfo['BROWSER'] = 'konqu'; 03956 } elseif (strpos($useragent, 'Opera') !== FALSE) { 03957 $bInfo['BROWSER'] = 'opera'; 03958 } elseif (strpos($useragent, 'MSIE') !== FALSE) { 03959 $bInfo['BROWSER'] = 'msie'; 03960 } elseif (strpos($useragent, 'Mozilla') !== FALSE) { 03961 $bInfo['BROWSER'] = 'net'; 03962 } elseif (strpos($useragent, 'Flash') !== FALSE) { 03963 $bInfo['BROWSER'] = 'flash'; 03964 } 03965 if ($bInfo['BROWSER']) { 03966 // Browser version 03967 switch ($bInfo['BROWSER']) { 03968 case 'net': 03969 $bInfo['VERSION'] = doubleval(substr($useragent, 8)); 03970 if (strpos($useragent, 'Netscape6/') !== FALSE) { 03971 $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape6/'), 10)); 03972 } // Will we ever know if this was a typo or intention...?! :-( 03973 if (strpos($useragent, 'Netscape/6') !== FALSE) { 03974 $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/6'), 10)); 03975 } 03976 if (strpos($useragent, 'Netscape/7') !== FALSE) { 03977 $bInfo['VERSION'] = doubleval(substr(strstr($useragent, 'Netscape/7'), 9)); 03978 } 03979 break; 03980 case 'msie': 03981 $tmp = strstr($useragent, 'MSIE'); 03982 $bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 4))); 03983 break; 03984 case 'opera': 03985 $tmp = strstr($useragent, 'Opera'); 03986 $bInfo['VERSION'] = doubleval(preg_replace('/^[^0-9]*/', '', substr($tmp, 5))); 03987 break; 03988 case 'konqu': 03989 $tmp = strstr($useragent, 'Konqueror/'); 03990 $bInfo['VERSION'] = doubleval(substr($tmp, 10)); 03991 break; 03992 } 03993 // Client system 03994 if (strpos($useragent, 'Win') !== FALSE) { 03995 $bInfo['SYSTEM'] = 'win'; 03996 } elseif (strpos($useragent, 'Mac') !== FALSE) { 03997 $bInfo['SYSTEM'] = 'mac'; 03998 } elseif (strpos($useragent, 'Linux') !== FALSE || strpos($useragent, 'X11') !== FALSE || strpos($useragent, 'SGI') !== FALSE || strpos($useragent, ' SunOS ') !== FALSE || strpos($useragent, ' HP-UX ') !== FALSE) { 03999 $bInfo['SYSTEM'] = 'unix'; 04000 } 04001 } 04002 // Is true if the browser supports css to format forms, especially the width 04003 $bInfo['FORMSTYLE'] = ($bInfo['BROWSER'] == 'msie' || ($bInfo['BROWSER'] == 'net' && $bInfo['VERSION'] >= 5) || $bInfo['BROWSER'] == 'opera' || $bInfo['BROWSER'] == 'konqu'); 04004 04005 return $bInfo; 04006 } 04007 04008 /** 04009 * Get the fully-qualified domain name of the host. 04010 * Usage: 2 04011 * 04012 * @param boolean Use request host (when not in CLI mode). 04013 * @return string The fully-qualified host name. 04014 */ 04015 public static function getHostname($requestHost = TRUE) { 04016 $host = ''; 04017 // If not called from the command-line, resolve on getIndpEnv() 04018 // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined 04019 if ($requestHost && (!defined('TYPO3_cliMode') || !TYPO3_cliMode)) { 04020 $host = self::getIndpEnv('HTTP_HOST'); 04021 } 04022 if (!$host) { 04023 // will fail for PHP 4.1 and 4.2 04024 $host = @php_uname('n'); 04025 // 'n' is ignored in broken installations 04026 if (strpos($host, ' ')) { 04027 $host = ''; 04028 } 04029 } 04030 // we have not found a FQDN yet 04031 if ($host && strpos($host, '.') === FALSE) { 04032 $ip = gethostbyname($host); 04033 // we got an IP address 04034 if ($ip != $host) { 04035 $fqdn = gethostbyaddr($ip); 04036 if ($ip != $fqdn) { 04037 $host = $fqdn; 04038 } 04039 } 04040 } 04041 if (!$host) { 04042 $host = 'localhost.localdomain'; 04043 } 04044 04045 return $host; 04046 } 04047 04048 04049 /************************* 04050 * 04051 * TYPO3 SPECIFIC FUNCTIONS 04052 * 04053 *************************/ 04054 04055 /** 04056 * Returns the absolute filename of a relative reference, resolves the "EXT:" prefix (way of referring to files inside extensions) and checks that the file is inside the PATH_site of the TYPO3 installation and implies a check with t3lib_div::validPathStr(). Returns false if checks failed. Does not check if the file exists. 04057 * Usage: 24 04058 * 04059 * @param string The input filename/filepath to evaluate 04060 * @param boolean If $onlyRelative is set (which it is by default), then only return values relative to the current PATH_site is accepted. 04061 * @param boolean If $relToTYPO3_mainDir is set, then relative paths are relative to PATH_typo3 constant - otherwise (default) they are relative to PATH_site 04062 * @return string Returns the absolute filename of $filename IF valid, otherwise blank string. 04063 */ 04064 public static function getFileAbsFileName($filename, $onlyRelative = TRUE, $relToTYPO3_mainDir = FALSE) { 04065 if (!strcmp($filename, '')) { 04066 return ''; 04067 } 04068 04069 if ($relToTYPO3_mainDir) { 04070 if (!defined('PATH_typo3')) { 04071 return ''; 04072 } 04073 $relPathPrefix = PATH_typo3; 04074 } else { 04075 $relPathPrefix = PATH_site; 04076 } 04077 if (substr($filename, 0, 4) == 'EXT:') { // extension 04078 list($extKey, $local) = explode('/', substr($filename, 4), 2); 04079 $filename = ''; 04080 if (strcmp($extKey, '') && t3lib_extMgm::isLoaded($extKey) && strcmp($local, '')) { 04081 $filename = t3lib_extMgm::extPath($extKey) . $local; 04082 } 04083 } elseif (!self::isAbsPath($filename)) { // relative. Prepended with $relPathPrefix 04084 $filename = $relPathPrefix . $filename; 04085 } elseif ($onlyRelative && !self::isFirstPartOfStr($filename, $relPathPrefix)) { // absolute, but set to blank if not allowed 04086 $filename = ''; 04087 } 04088 if (strcmp($filename, '') && self::validPathStr($filename)) { // checks backpath. 04089 return $filename; 04090 } 04091 } 04092 04093 /** 04094 * Checks for malicious file paths. 04095 * 04096 * Returns TRUE if no '//', '..', '\' or control characters are found in the $theFile. 04097 * This should make sure that the path is not pointing 'backwards' and further doesn't contain double/back slashes. 04098 * So it's compatible with the UNIX style path strings valid for TYPO3 internally. 04099 * Usage: 14 04100 * 04101 * @param string Filepath to evaluate 04102 * @return boolean TRUE, $theFile is allowed path string 04103 * @see http://php.net/manual/en/security.filesystem.nullbytes.php 04104 * @todo Possible improvement: Should it rawurldecode the string first to check if any of these characters is encoded ? 04105 */ 04106 public static function validPathStr($theFile) { 04107 if (strpos($theFile, '//') === FALSE && strpos($theFile, '\\') === FALSE && !preg_match('#(?:^\.\.|/\.\./|[[:cntrl:]])#', $theFile)) { 04108 return TRUE; 04109 } 04110 } 04111 04112 /** 04113 * Checks if the $path is absolute or relative (detecting either '/' or 'x:/' as first part of string) and returns true if so. 04114 * Usage: 8 04115 * 04116 * @param string Filepath to evaluate 04117 * @return boolean 04118 */ 04119 public static function isAbsPath($path) { 04120 return TYPO3_OS == 'WIN' ? substr($path, 1, 2) == ':/' : substr($path, 0, 1) == '/'; 04121 } 04122 04123 /** 04124 * Returns true if the path is absolute, without backpath '..' and within the PATH_site OR within the lockRootPath 04125 * Usage: 5 04126 * 04127 * @param string Filepath to evaluate 04128 * @return boolean 04129 */ 04130 public static function isAllowedAbsPath($path) { 04131 if (self::isAbsPath($path) && 04132 self::validPathStr($path) && 04133 (self::isFirstPartOfStr($path, PATH_site) 04134 || 04135 ($GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && self::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'])) 04136 ) 04137 ) { 04138 return TRUE; 04139 } 04140 } 04141 04142 /** 04143 * Verifies the input filename againts the 'fileDenyPattern'. Returns true if OK. 04144 * Usage: 2 04145 * 04146 * @param string Filepath to evaluate 04147 * @return boolean 04148 */ 04149 public static function verifyFilenameAgainstDenyPattern($filename) { 04150 // Filenames are not allowed to contain control characters 04151 if (preg_match('/[[:cntrl:]]/', $filename)) { 04152 return FALSE; 04153 } 04154 04155 if (strcmp($filename, '') && strcmp($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'], '')) { 04156 $result = preg_match('/' . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'] . '/i', $filename); 04157 if ($result) { 04158 return FALSE; 04159 } // so if a matching filename is found, return FALSE; 04160 } 04161 return TRUE; 04162 } 04163 04164 /** 04165 * Checks if a given string is a valid frame URL to be loaded in the 04166 * backend. 04167 * 04168 * @param string $url potential URL to check 04169 * 04170 * @return string either $url if $url is considered to be harmless, or an 04171 * empty string otherwise 04172 */ 04173 public static function sanitizeLocalUrl($url = '') { 04174 $sanitizedUrl = ''; 04175 $decodedUrl = rawurldecode($url); 04176 04177 if (!empty($url) && self::removeXSS($decodedUrl) === $decodedUrl) { 04178 $testAbsoluteUrl = self::resolveBackPath($decodedUrl); 04179 $testRelativeUrl = self::resolveBackPath( 04180 self::dirname(self::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl 04181 ); 04182 04183 // Pass if URL is on the current host: 04184 if (self::isValidUrl($decodedUrl)) { 04185 if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) { 04186 $sanitizedUrl = $url; 04187 } 04188 // Pass if URL is an absolute file path: 04189 } elseif (self::isAbsPath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) { 04190 $sanitizedUrl = $url; 04191 // Pass if URL is absolute and below TYPO3 base directory: 04192 } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && substr($decodedUrl, 0, 1) === '/') { 04193 $sanitizedUrl = $url; 04194 // Pass if URL is relative and below TYPO3 base directory: 04195 } elseif (strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && substr($decodedUrl, 0, 1) !== '/') { 04196 $sanitizedUrl = $url; 04197 } 04198 } 04199 04200 if (!empty($url) && empty($sanitizedUrl)) { 04201 self::sysLog('The URL "' . $url . '" is not considered to be local and was denied.', 'Core', self::SYSLOG_SEVERITY_NOTICE); 04202 } 04203 04204 return $sanitizedUrl; 04205 } 04206 04207 /** 04208 * Moves $source file to $destination if uploaded, otherwise try to make a copy 04209 * Usage: 4 04210 * 04211 * @param string Source file, absolute path 04212 * @param string Destination file, absolute path 04213 * @return boolean Returns true if the file was moved. 04214 * @coauthor Dennis Petersen <fessor@software.dk> 04215 * @see upload_to_tempfile() 04216 */ 04217 public static function upload_copy_move($source, $destination) { 04218 if (is_uploaded_file($source)) { 04219 $uploaded = TRUE; 04220 // Return the value of move_uploaded_file, and if false the temporary $source is still around so the user can use unlink to delete it: 04221 $uploadedResult = move_uploaded_file($source, $destination); 04222 } else { 04223 $uploaded = FALSE; 04224 @copy($source, $destination); 04225 } 04226 04227 self::fixPermissions($destination); // Change the permissions of the file 04228 04229 // If here the file is copied and the temporary $source is still around, so when returning false the user can try unlink to delete the $source 04230 return $uploaded ? $uploadedResult : FALSE; 04231 } 04232 04233 /** 04234 * Will move an uploaded file (normally in "/tmp/xxxxx") to a temporary filename in PATH_site."typo3temp/" from where TYPO3 can use it under safe_mode. 04235 * Use this function to move uploaded files to where you can work on them. 04236 * REMEMBER to use t3lib_div::unlink_tempfile() afterwards - otherwise temp-files will build up! They are NOT automatically deleted in PATH_site."typo3temp/"! 04237 * Usage: 6 04238 * 04239 * @param string The temporary uploaded filename, eg. $_FILES['[upload field name here]']['tmp_name'] 04240 * @return string If a new file was successfully created, return its filename, otherwise blank string. 04241 * @see unlink_tempfile(), upload_copy_move() 04242 */ 04243 public static function upload_to_tempfile($uploadedFileName) { 04244 if (is_uploaded_file($uploadedFileName)) { 04245 $tempFile = self::tempnam('upload_temp_'); 04246 move_uploaded_file($uploadedFileName, $tempFile); 04247 return @is_file($tempFile) ? $tempFile : ''; 04248 } 04249 } 04250 04251 /** 04252 * Deletes (unlink) a temporary filename in 'PATH_site."typo3temp/"' given as input. 04253 * The function will check that the file exists, is in PATH_site."typo3temp/" and does not contain back-spaces ("../") so it should be pretty safe. 04254 * Use this after upload_to_tempfile() or tempnam() from this class! 04255 * Usage: 9 04256 * 04257 * @param string Filepath for a file in PATH_site."typo3temp/". Must be absolute. 04258 * @return boolean Returns true if the file was unlink()'ed 04259 * @see upload_to_tempfile(), tempnam() 04260 */ 04261 public static function unlink_tempfile($uploadedTempFileName) { 04262 if ($uploadedTempFileName && self::validPathStr($uploadedTempFileName) && self::isFirstPartOfStr($uploadedTempFileName, PATH_site . 'typo3temp/') && @is_file($uploadedTempFileName)) { 04263 if (unlink($uploadedTempFileName)) { 04264 return TRUE; 04265 } 04266 } 04267 } 04268 04269 /** 04270 * Create temporary filename (Create file with unique file name) 04271 * This function should be used for getting temporary filenames - will make your applications safe for open_basedir = on 04272 * REMEMBER to delete the temporary files after use! This is done by t3lib_div::unlink_tempfile() 04273 * Usage: 7 04274 * 04275 * @param string Prefix to temp file (which will have no extension btw) 04276 * @return string result from PHP function tempnam() with PATH_site.'typo3temp/' set for temp path. 04277 * @see unlink_tempfile(), upload_to_tempfile() 04278 */ 04279 public static function tempnam($filePrefix) { 04280 return tempnam(PATH_site . 'typo3temp/', $filePrefix); 04281 } 04282 04283 /** 04284 * Standard authentication code (used in Direct Mail, checkJumpUrl and setfixed links computations) 04285 * Usage: 2 04286 * 04287 * @param mixed Uid (integer) or record (array) 04288 * @param string List of fields from the record if that is given. 04289 * @param integer Length of returned authentication code. 04290 * @return string MD5 hash of 8 chars. 04291 */ 04292 public static function stdAuthCode($uid_or_record, $fields = '', $codeLength = 8) { 04293 04294 if (is_array($uid_or_record)) { 04295 $recCopy_temp = array(); 04296 if ($fields) { 04297 $fieldArr = self::trimExplode(',', $fields, 1); 04298 foreach ($fieldArr as $k => $v) { 04299 $recCopy_temp[$k] = $uid_or_record[$v]; 04300 } 04301 } else { 04302 $recCopy_temp = $uid_or_record; 04303 } 04304 $preKey = implode('|', $recCopy_temp); 04305 } else { 04306 $preKey = $uid_or_record; 04307 } 04308 04309 $authCode = $preKey . '||' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']; 04310 $authCode = substr(md5($authCode), 0, $codeLength); 04311 return $authCode; 04312 } 04313 04314 /** 04315 * Splits the input query-parameters into an array with certain parameters filtered out. 04316 * Used to create the cHash value 04317 * 04318 * @param string Query-parameters: "&xxx=yyy&zzz=uuu" 04319 * @return array Array with key/value pairs of query-parameters WITHOUT a certain list of variable names (like id, type, no_cache etc.) and WITH a variable, encryptionKey, specific for this server/installation 04320 * @see tslib_fe::makeCacheHash(), tslib_cObj::typoLink(), t3lib_div::calculateCHash() 04321 */ 04322 public static function cHashParams($addQueryParams) { 04323 $params = explode('&', substr($addQueryParams, 1)); // Splitting parameters up 04324 04325 // Make array: 04326 $pA = array(); 04327 foreach ($params as $theP) { 04328 $pKV = explode('=', $theP); // Splitting single param by '=' sign 04329 if (!self::inList('id,type,no_cache,cHash,MP,ftu', $pKV[0]) && !preg_match('/TSFE_ADMIN_PANEL\[.*?\]/', $pKV[0])) { 04330 $pA[rawurldecode($pKV[0])] = (string) rawurldecode($pKV[1]); 04331 } 04332 } 04333 // Hook: Allows to manipulate the parameters which are taken to build the chash: 04334 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['cHashParamsHook'])) { 04335 $cHashParamsHook =& $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_div.php']['cHashParamsHook']; 04336 if (is_array($cHashParamsHook)) { 04337 $hookParameters = array( 04338 'addQueryParams' => &$addQueryParams, 04339 'params' => &$params, 04340 'pA' => &$pA, 04341 ); 04342 $hookReference = NULL; 04343 foreach ($cHashParamsHook as $hookFunction) { 04344 self::callUserFunction($hookFunction, $hookParameters, $hookReference); 04345 } 04346 } 04347 } 04348 // Finish and sort parameters array by keys: 04349 $pA['encryptionKey'] = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']; 04350 ksort($pA); 04351 04352 return $pA; 04353 } 04354 04355 /** 04356 * Returns the cHash based on provided query parameters and added values from internal call 04357 * 04358 * @param string Query-parameters: "&xxx=yyy&zzz=uuu" 04359 * @return string Hash of all the values 04360 * @see t3lib_div::cHashParams(), t3lib_div::calculateCHash() 04361 */ 04362 public static function generateCHash($addQueryParams) { 04363 $cHashParams = self::cHashParams($addQueryParams); 04364 $cHash = self::calculateCHash($cHashParams); 04365 return $cHash; 04366 } 04367 04368 /** 04369 * Calculates the cHash based on the provided parameters 04370 * 04371 * @param array Array of key-value pairs 04372 * @return string Hash of all the values 04373 */ 04374 public static function calculateCHash($params) { 04375 $cHash = md5(serialize($params)); 04376 return $cHash; 04377 } 04378 04379 /** 04380 * Responds on input localization setting value whether the page it comes from should be hidden if no translation exists or not. 04381 * 04382 * @param integer Value from "l18n_cfg" field of a page record 04383 * @return boolean True if the page should be hidden 04384 */ 04385 public static function hideIfNotTranslated($l18n_cfg_fieldValue) { 04386 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['hidePagesIfNotTranslatedByDefault']) { 04387 return $l18n_cfg_fieldValue & 2 ? FALSE : TRUE; 04388 } else { 04389 return $l18n_cfg_fieldValue & 2 ? TRUE : FALSE; 04390 } 04391 } 04392 04393 /** 04394 * Includes a locallang file and returns the $LOCAL_LANG array found inside. 04395 * 04396 * @param string Input is a file-reference (see t3lib_div::getFileAbsFileName). That file is expected to be a 'locallang.php' file containing a $LOCAL_LANG array (will be included!) or a 'locallang.xml' file conataining a valid XML TYPO3 language structure. 04397 * @param string Language key 04398 * @param string Character set (option); if not set, determined by the language key 04399 * @param integer Error mode (when file could not be found): 0 - syslog entry, 1 - do nothing, 2 - throw an exception 04400 * @return array Value of $LOCAL_LANG found in the included file. If that array is found it will returned. 04401 * Otherwise an empty array and it is FALSE in error case. 04402 */ 04403 public static function readLLfile($fileRef, $langKey, $charset = '', $errorMode = 0) { 04404 04405 $result = FALSE; 04406 $file = self::getFileAbsFileName($fileRef); 04407 if ($file) { 04408 $baseFile = preg_replace('/\.(php|xml)$/', '', $file); 04409 04410 if (@is_file($baseFile . '.xml')) { 04411 $LOCAL_LANG = self::readLLXMLfile($baseFile . '.xml', $langKey, $charset); 04412 } elseif (@is_file($baseFile . '.php')) { 04413 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] || $charset) { 04414 $LOCAL_LANG = self::readLLPHPfile($baseFile . '.php', $langKey, $charset); 04415 } else { 04416 include($baseFile . '.php'); 04417 if (is_array($LOCAL_LANG)) { 04418 $LOCAL_LANG = array('default' => $LOCAL_LANG['default'], $langKey => $LOCAL_LANG[$langKey]); 04419 } 04420 } 04421 } else { 04422 $errorMsg = 'File "' . $fileRef . '" not found!'; 04423 if ($errorMode == 2) { 04424 throw new t3lib_exception($errorMsg); 04425 } elseif (!$errorMode) { 04426 self::sysLog($errorMsg, 'Core', self::SYSLOG_SEVERITY_ERROR); 04427 } 04428 $fileNotFound = TRUE; 04429 } 04430 04431 04432 $overrides = array(); 04433 $fileRefWithoutExtension = preg_replace('/\.(php|xml)$/', '', $fileRef); 04434 04435 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRefWithoutExtension . '.php'])) { 04436 $overrides = array_merge($overrides, $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRefWithoutExtension . '.php']); 04437 } 04438 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRefWithoutExtension . '.xml'])) { 04439 $overrides = array_merge($overrides, $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRefWithoutExtension . '.xml']); 04440 } 04441 04442 if (count($overrides) > 0) { 04443 foreach ($overrides as $overrideFile) { 04444 $languageOverrideFileName = self::getFileAbsFileName($overrideFile); 04445 if (@is_file($languageOverrideFileName)) { 04446 $languageOverrideArray = self::readLLXMLfile($languageOverrideFileName, $langKey, $charset); 04447 $LOCAL_LANG = self::array_merge_recursive_overrule($LOCAL_LANG, $languageOverrideArray); 04448 } 04449 } 04450 } 04451 } 04452 if ($fileNotFound !== TRUE) { 04453 $result = is_array($LOCAL_LANG) ? $LOCAL_LANG : array(); 04454 } 04455 return $result; 04456 } 04457 04458 /** 04459 * Includes a locallang-php file and returns the $LOCAL_LANG array 04460 * Works only when the frontend or backend has been initialized with a charset conversion object. See first code lines. 04461 * 04462 * @param string Absolute reference to locallang-PHP file 04463 * @param string TYPO3 language key, eg. "dk" or "de" or "default" 04464 * @param string Character set (optional) 04465 * @return array LOCAL_LANG array in return. 04466 */ 04467 public static function readLLPHPfile($fileRef, $langKey, $charset = '') { 04468 04469 if (is_object($GLOBALS['LANG'])) { 04470 $csConvObj = $GLOBALS['LANG']->csConvObj; 04471 } elseif (is_object($GLOBALS['TSFE'])) { 04472 $csConvObj = $GLOBALS['TSFE']->csConvObj; 04473 } else { 04474 $csConvObj = self::makeInstance('t3lib_cs'); 04475 } 04476 04477 if (@is_file($fileRef) && $langKey) { 04478 04479 // Set charsets: 04480 $sourceCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); 04481 if ($charset) { 04482 $targetCharset = $csConvObj->parse_charset($charset); 04483 } elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { 04484 // when forceCharset is set, we store ALL labels in this charset!!! 04485 $targetCharset = $csConvObj->parse_charset($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']); 04486 } else { 04487 $targetCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); 04488 } 04489 04490 // Cache file name: 04491 $hashSource = substr($fileRef, strlen(PATH_site)) . '|' . date('d-m-Y H:i:s', filemtime($fileRef)) . '|version=2.3'; 04492 $cacheFileName = PATH_site . 'typo3temp/llxml/' . 04493 substr(basename($fileRef), 10, 15) . 04494 '_' . self::shortMD5($hashSource) . '.' . $langKey . '.' . $targetCharset . '.cache'; 04495 // Check if cache file exists... 04496 if (!@is_file($cacheFileName)) { // ... if it doesn't, create content and write it: 04497 $LOCAL_LANG = NULL; 04498 // Get PHP data 04499 include($fileRef); 04500 if (!is_array($LOCAL_LANG)) { 04501 $fileName = substr($fileRef, strlen(PATH_site)); 04502 throw new RuntimeException( 04503 'TYPO3 Fatal Error: "' . $fileName . '" is no TYPO3 language file!', 04504 1270853900 04505 ); 04506 } 04507 04508 // converting the default language (English) 04509 // this needs to be done for a few accented loan words and extension names 04510 if (is_array($LOCAL_LANG['default']) && $targetCharset != 'iso-8859-1') { 04511 foreach ($LOCAL_LANG['default'] as &$labelValue) { 04512 $labelValue = $csConvObj->conv($labelValue, 'iso-8859-1', $targetCharset); 04513 } 04514 } 04515 04516 if ($langKey != 'default' && is_array($LOCAL_LANG[$langKey]) && $sourceCharset != $targetCharset) { 04517 foreach ($LOCAL_LANG[$langKey] as &$labelValue) { 04518 $labelValue = $csConvObj->conv($labelValue, $sourceCharset, $targetCharset); 04519 } 04520 } 04521 04522 // Cache the content now: 04523 $serContent = array('origFile' => $hashSource, 'LOCAL_LANG' => array('default' => $LOCAL_LANG['default'], $langKey => $LOCAL_LANG[$langKey])); 04524 $res = self::writeFileToTypo3tempDir($cacheFileName, serialize($serContent)); 04525 if ($res) { 04526 throw new RuntimeException( 04527 'TYPO3 Fatal Error: "' . $res, 04528 1270853901 04529 ); 04530 } 04531 } else { 04532 // Get content from cache: 04533 $serContent = unserialize(self::getUrl($cacheFileName)); 04534 $LOCAL_LANG = $serContent['LOCAL_LANG']; 04535 } 04536 04537 return $LOCAL_LANG; 04538 } 04539 } 04540 04541 /** 04542 * Includes a locallang-xml file and returns the $LOCAL_LANG array 04543 * Works only when the frontend or backend has been initialized with a charset conversion object. See first code lines. 04544 * 04545 * @param string Absolute reference to locallang-XML file 04546 * @param string TYPO3 language key, eg. "dk" or "de" or "default" 04547 * @param string Character set (optional) 04548 * @return array LOCAL_LANG array in return. 04549 */ 04550 public static function readLLXMLfile($fileRef, $langKey, $charset = '') { 04551 04552 if (is_object($GLOBALS['LANG'])) { 04553 $csConvObj = $GLOBALS['LANG']->csConvObj; 04554 } elseif (is_object($GLOBALS['TSFE'])) { 04555 $csConvObj = $GLOBALS['TSFE']->csConvObj; 04556 } else { 04557 $csConvObj = self::makeInstance('t3lib_cs'); 04558 } 04559 04560 $LOCAL_LANG = NULL; 04561 if (@is_file($fileRef) && $langKey) { 04562 04563 // Set charset: 04564 if ($charset) { 04565 $targetCharset = $csConvObj->parse_charset($charset); 04566 } elseif ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { 04567 // when forceCharset is set, we store ALL labels in this charset!!! 04568 $targetCharset = $csConvObj->parse_charset($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']); 04569 } else { 04570 $targetCharset = $csConvObj->parse_charset($csConvObj->charSetArray[$langKey] ? $csConvObj->charSetArray[$langKey] : 'iso-8859-1'); 04571 } 04572 04573 // Cache file name: 04574 $hashSource = substr($fileRef, strlen(PATH_site)) . '|' . date('d-m-Y H:i:s', filemtime($fileRef)) . '|version=2.3'; 04575 $cacheFileName = PATH_site . 'typo3temp/llxml/' . 04576 substr(basename($fileRef), 10, 15) . 04577 '_' . self::shortMD5($hashSource) . '.' . $langKey . '.' . $targetCharset . '.cache'; 04578 04579 // Check if cache file exists... 04580 if (!@is_file($cacheFileName)) { // ... if it doesn't, create content and write it: 04581 04582 // Read XML, parse it. 04583 $xmlString = self::getUrl($fileRef); 04584 $xmlContent = self::xml2array($xmlString); 04585 if (!is_array($xmlContent)) { 04586 $fileName = substr($fileRef, strlen(PATH_site)); 04587 throw new RuntimeException( 04588 'TYPO3 Fatal Error: The file "' . $fileName . '" is no TYPO3 language file!', 04589 1270853902 04590 ); 04591 } 04592 04593 // Set default LOCAL_LANG array content: 04594 $LOCAL_LANG = array(); 04595 $LOCAL_LANG['default'] = $xmlContent['data']['default']; 04596 04597 // converting the default language (English) 04598 // this needs to be done for a few accented loan words and extension names 04599 // NOTE: no conversion is done when in UTF-8 mode! 04600 if (is_array($LOCAL_LANG['default']) && $targetCharset != 'utf-8') { 04601 foreach ($LOCAL_LANG['default'] as &$labelValue) { 04602 $labelValue = $csConvObj->utf8_decode($labelValue, $targetCharset); 04603 } 04604 unset($labelValue); 04605 } 04606 04607 // converting other languages to their "native" charsets 04608 // NOTE: no conversion is done when in UTF-8 mode! 04609 if ($langKey != 'default') { 04610 04611 // If no entry is found for the language key, then force a value depending on meta-data setting. By default an automated filename will be used: 04612 $LOCAL_LANG[$langKey] = self::llXmlAutoFileName($fileRef, $langKey); 04613 $localized_file = self::getFileAbsFileName($LOCAL_LANG[$langKey]); 04614 if (!@is_file($localized_file) && isset($xmlContent['data'][$langKey])) { 04615 $LOCAL_LANG[$langKey] = $xmlContent['data'][$langKey]; 04616 } 04617 04618 // Checking if charset should be converted. 04619 if (is_array($LOCAL_LANG[$langKey]) && $targetCharset != 'utf-8') { 04620 foreach ($LOCAL_LANG[$langKey] as $labelKey => $labelValue) { 04621 $LOCAL_LANG[$langKey][$labelKey] = $csConvObj->utf8_decode($labelValue, $targetCharset); 04622 } 04623 } 04624 } 04625 04626 // Cache the content now: 04627 $serContent = array('origFile' => $hashSource, 'LOCAL_LANG' => array('default' => $LOCAL_LANG['default'], $langKey => $LOCAL_LANG[$langKey])); 04628 $res = self::writeFileToTypo3tempDir($cacheFileName, serialize($serContent)); 04629 if ($res) { 04630 throw new RuntimeException( 04631 'TYPO3 Fatal Error: ' . $res, 04632 1270853903 04633 ); 04634 } 04635 } else { 04636 // Get content from cache: 04637 $serContent = unserialize(self::getUrl($cacheFileName)); 04638 $LOCAL_LANG = $serContent['LOCAL_LANG']; 04639 } 04640 04641 // Checking for EXTERNAL file for non-default language: 04642 if ($langKey != 'default' && is_string($LOCAL_LANG[$langKey]) && strlen($LOCAL_LANG[$langKey])) { 04643 04644 // Look for localized file: 04645 $localized_file = self::getFileAbsFileName($LOCAL_LANG[$langKey]); 04646 if ($localized_file && @is_file($localized_file)) { 04647 04648 // Cache file name: 04649 $hashSource = substr($localized_file, strlen(PATH_site)) . '|' . date('d-m-Y H:i:s', filemtime($localized_file)) . '|version=2.3'; 04650 $cacheFileName = PATH_site . 'typo3temp/llxml/EXT_' . 04651 substr(basename($localized_file), 10, 15) . 04652 '_' . self::shortMD5($hashSource) . '.' . $langKey . '.' . $targetCharset . '.cache'; 04653 04654 // Check if cache file exists... 04655 if (!@is_file($cacheFileName)) { // ... if it doesn't, create content and write it: 04656 04657 // Read and parse XML content: 04658 $local_xmlString = self::getUrl($localized_file); 04659 $local_xmlContent = self::xml2array($local_xmlString); 04660 if (!is_array($local_xmlContent)) { 04661 $fileName = substr($localized_file, strlen(PATH_site)); 04662 throw new RuntimeException( 04663 'TYPO3 Fatal Error: The file "' . $fileName . '" is no TYPO3 language file!', 04664 1270853904 04665 ); 04666 } 04667 $LOCAL_LANG[$langKey] = is_array($local_xmlContent['data'][$langKey]) ? $local_xmlContent['data'][$langKey] : array(); 04668 04669 // Checking if charset should be converted. 04670 if (is_array($LOCAL_LANG[$langKey]) && $targetCharset != 'utf-8') { 04671 foreach ($LOCAL_LANG[$langKey] as $labelKey => $labelValue) { 04672 $LOCAL_LANG[$langKey][$labelKey] = $csConvObj->utf8_decode($labelValue, $targetCharset); 04673 } 04674 } 04675 04676 // Cache the content now: 04677 $serContent = array('extlang' => $langKey, 'origFile' => $hashSource, 'EXT_DATA' => $LOCAL_LANG[$langKey]); 04678 $res = self::writeFileToTypo3tempDir($cacheFileName, serialize($serContent)); 04679 if ($res) { 04680 throw new RuntimeException( 04681 'TYPO3 Fatal Error: ' . $res, 04682 1270853905 04683 ); 04684 } 04685 } else { 04686 // Get content from cache: 04687 $serContent = unserialize(self::getUrl($cacheFileName)); 04688 $LOCAL_LANG[$langKey] = $serContent['EXT_DATA']; 04689 } 04690 } else { 04691 $LOCAL_LANG[$langKey] = array(); 04692 } 04693 } 04694 04695 return $LOCAL_LANG; 04696 } 04697 } 04698 04699 /** 04700 * Returns auto-filename for locallang-XML localizations. 04701 * 04702 * @param string Absolute file reference to locallang-XML file. Must be inside system/global/local extension 04703 * @param string Language key 04704 * @return string Returns the filename reference for the language unless error occured (or local mode is used) in which case it will be NULL 04705 */ 04706 public static function llXmlAutoFileName($fileRef, $language) { 04707 // Analyse file reference: 04708 $location = 'typo3conf/l10n/' . $language . '/'; // Default location of translations 04709 if (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'sysext/')) { // Is system: 04710 $validatedPrefix = PATH_typo3 . 'sysext/'; 04711 #$location = 'EXT:csh_'.$language.'/'; // For system extensions translations are found in "csh_*" extensions (language packs) 04712 } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3 . 'ext/')) { // Is global: 04713 $validatedPrefix = PATH_typo3 . 'ext/'; 04714 } elseif (self::isFirstPartOfStr($fileRef, PATH_typo3conf . 'ext/')) { // Is local: 04715 $validatedPrefix = PATH_typo3conf . 'ext/'; 04716 } else { 04717 $validatedPrefix = ''; 04718 } 04719 04720 if ($validatedPrefix) { 04721 04722 // Divide file reference into extension key, directory (if any) and base name: 04723 list($file_extKey, $file_extPath) = explode('/', substr($fileRef, strlen($validatedPrefix)), 2); 04724 $temp = self::revExplode('/', $file_extPath, 2); 04725 if (count($temp) == 1) { 04726 array_unshift($temp, ''); 04727 } // Add empty first-entry if not there. 04728 list($file_extPath, $file_fileName) = $temp; 04729 04730 // The filename is prefixed with "[language key]." because it prevents the llxmltranslate tool from detecting it. 04731 return $location . 04732 $file_extKey . '/' . 04733 ($file_extPath ? $file_extPath . '/' : '') . 04734 $language . '.' . $file_fileName; 04735 } else { 04736 return NULL; 04737 } 04738 } 04739 04740 04741 /** 04742 * Loads the $TCA (Table Configuration Array) for the $table 04743 * 04744 * Requirements: 04745 * 1) must be configured table (the ctrl-section configured), 04746 * 2) columns must not be an array (which it is always if whole table loaded), and 04747 * 3) there is a value for dynamicConfigFile (filename in typo3conf) 04748 * 04749 * Note: For the frontend this loads only 'ctrl' and 'feInterface' parts. 04750 * For complete TCA use $GLOBALS['TSFE']->includeTCA() instead. 04751 * 04752 * Usage: 84 04753 * 04754 * @param string Table name for which to load the full TCA array part into the global $TCA 04755 * @return void 04756 */ 04757 public static function loadTCA($table) { 04758 global $TCA; 04759 04760 if (isset($TCA[$table])) { 04761 $tca = &$TCA[$table]; 04762 if (!$tca['columns']) { 04763 $dcf = $tca['ctrl']['dynamicConfigFile']; 04764 if ($dcf) { 04765 if (!strcmp(substr($dcf, 0, 6), 'T3LIB:')) { 04766 include(PATH_t3lib . 'stddb/' . substr($dcf, 6)); 04767 } elseif (self::isAbsPath($dcf) && @is_file($dcf)) { // Absolute path... 04768 include($dcf); 04769 } else { 04770 include(PATH_typo3conf . $dcf); 04771 } 04772 } 04773 } 04774 } 04775 } 04776 04777 /** 04778 * Looks for a sheet-definition in the input data structure array. If found it will return the data structure for the sheet given as $sheet (if found). 04779 * If the sheet definition is in an external file that file is parsed and the data structure inside of that is returned. 04780 * Usage: 5 04781 * 04782 * @param array Input data structure, possibly with a sheet-definition and references to external data source files. 04783 * @param string The sheet to return, preferably. 04784 * @return array An array with two num. keys: key0: The data structure is returned in this key (array) UNLESS an error happend in which case an error string is returned (string). key1: The used sheet key value! 04785 */ 04786 public static function resolveSheetDefInDS($dataStructArray, $sheet = 'sDEF') { 04787 if (!is_array($dataStructArray)) { 04788 return 'Data structure must be an array'; 04789 } 04790 04791 if (is_array($dataStructArray['sheets'])) { 04792 $singleSheet = FALSE; 04793 if (!isset($dataStructArray['sheets'][$sheet])) { 04794 $sheet = 'sDEF'; 04795 } 04796 $dataStruct = $dataStructArray['sheets'][$sheet]; 04797 04798 // If not an array, but still set, then regard it as a relative reference to a file: 04799 if ($dataStruct && !is_array($dataStruct)) { 04800 $file = self::getFileAbsFileName($dataStruct); 04801 if ($file && @is_file($file)) { 04802 $dataStruct = self::xml2array(self::getUrl($file)); 04803 } 04804 } 04805 } else { 04806 $singleSheet = TRUE; 04807 $dataStruct = $dataStructArray; 04808 if (isset($dataStruct['meta'])) { 04809 unset($dataStruct['meta']); 04810 } // Meta data should not appear there. 04811 $sheet = 'sDEF'; // Default sheet 04812 } 04813 return array($dataStruct, $sheet, $singleSheet); 04814 } 04815 04816 /** 04817 * Resolves ALL sheet definitions in dataStructArray 04818 * If no sheet is found, then the default "sDEF" will be created with the dataStructure inside. 04819 * 04820 * @param array Input data structure, possibly with a sheet-definition and references to external data source files. 04821 * @return array Output data structure with all sheets resolved as arrays. 04822 */ 04823 public static function resolveAllSheetsInDS(array $dataStructArray) { 04824 if (is_array($dataStructArray['sheets'])) { 04825 $out = array('sheets' => array()); 04826 foreach ($dataStructArray['sheets'] as $sheetId => $sDat) { 04827 list($ds, $aS) = self::resolveSheetDefInDS($dataStructArray, $sheetId); 04828 if ($sheetId == $aS) { 04829 $out['sheets'][$aS] = $ds; 04830 } 04831 } 04832 } else { 04833 list($ds) = self::resolveSheetDefInDS($dataStructArray); 04834 $out = array('sheets' => array('sDEF' => $ds)); 04835 } 04836 return $out; 04837 } 04838 04839 /** 04840 * Calls a userdefined function/method in class 04841 * Such a function/method should look like this: "function proc(&$params, &$ref) {...}" 04842 * Usage: 17 04843 * 04844 * @param string Function/Method reference, '[file-reference":"]["&"]class/function["->"method-name]'. You can prefix this reference with "[file-reference]:" and t3lib_div::getFileAbsFileName() will then be used to resolve the filename and subsequently include it by "require_once()" which means you don't have to worry about including the class file either! Example: "EXT:realurl/class.tx_realurl.php:&tx_realurl->encodeSpURL". Finally; you can prefix the class name with "&" if you want to reuse a former instance of the same object call ("singleton"). 04845 * @param mixed Parameters to be pass along (typically an array) (REFERENCE!) 04846 * @param mixed Reference to be passed along (typically "$this" - being a reference to the calling object) (REFERENCE!) 04847 * @param string Required prefix of class or function name 04848 * @param integer Error mode (when class/function could not be found): 0 - call debug(), 1 - do nothing, 2 - raise an exception (allows to call a user function that may return FALSE) 04849 * @return mixed Content from method/function call or false if the class/method/function was not found 04850 * @see getUserObj() 04851 */ 04852 public static function callUserFunction($funcName, &$params, &$ref, $checkPrefix = 'user_', $errorMode = 0) { 04853 global $TYPO3_CONF_VARS; 04854 $content = FALSE; 04855 04856 // Check persistent object and if found, call directly and exit. 04857 if (is_array($GLOBALS['T3_VAR']['callUserFunction'][$funcName])) { 04858 return call_user_func_array( 04859 array(&$GLOBALS['T3_VAR']['callUserFunction'][$funcName]['obj'], 04860 $GLOBALS['T3_VAR']['callUserFunction'][$funcName]['method']), 04861 array(&$params, &$ref) 04862 ); 04863 } 04864 04865 // Check file-reference prefix; if found, require_once() the file (should be library of code) 04866 if (strpos($funcName, ':') !== FALSE) { 04867 list($file, $funcRef) = self::revExplode(':', $funcName, 2); 04868 $requireFile = self::getFileAbsFileName($file); 04869 if ($requireFile) { 04870 self::requireOnce($requireFile); 04871 } 04872 } else { 04873 $funcRef = $funcName; 04874 } 04875 04876 // Check for persistent object token, "&" 04877 if (substr($funcRef, 0, 1) == '&') { 04878 $funcRef = substr($funcRef, 1); 04879 $storePersistentObject = TRUE; 04880 } else { 04881 $storePersistentObject = FALSE; 04882 } 04883 04884 // Check prefix is valid: 04885 if ($checkPrefix && 04886 !self::isFirstPartOfStr(trim($funcRef), $checkPrefix) && 04887 !self::isFirstPartOfStr(trim($funcRef), 'tx_') 04888 ) { 04889 $errorMsg = "Function/class '$funcRef' was not prepended with '$checkPrefix'"; 04890 if ($errorMode == 2) { 04891 throw new Exception($errorMsg); 04892 } elseif (!$errorMode) { 04893 debug($errorMsg, 't3lib_div::callUserFunction'); 04894 } 04895 return FALSE; 04896 } 04897 04898 // Call function or method: 04899 $parts = explode('->', $funcRef); 04900 if (count($parts) == 2) { // Class 04901 04902 // Check if class/method exists: 04903 if (class_exists($parts[0])) { 04904 04905 // Get/Create object of class: 04906 if ($storePersistentObject) { // Get reference to current instance of class: 04907 if (!is_object($GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]])) { 04908 $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = self::makeInstance($parts[0]); 04909 } 04910 $classObj = $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]]; 04911 } else { // Create new object: 04912 $classObj = self::makeInstance($parts[0]); 04913 } 04914 04915 if (method_exists($classObj, $parts[1])) { 04916 04917 // If persistent object should be created, set reference: 04918 if ($storePersistentObject) { 04919 $GLOBALS['T3_VAR']['callUserFunction'][$funcName] = array( 04920 'method' => $parts[1], 04921 'obj' => &$classObj 04922 ); 04923 } 04924 // Call method: 04925 $content = call_user_func_array( 04926 array(&$classObj, $parts[1]), 04927 array(&$params, &$ref) 04928 ); 04929 } else { 04930 $errorMsg = "<strong>ERROR:</strong> No method name '" . $parts[1] . "' in class " . $parts[0]; 04931 if ($errorMode == 2) { 04932 throw new Exception($errorMsg); 04933 } elseif (!$errorMode) { 04934 debug($errorMsg, 't3lib_div::callUserFunction'); 04935 } 04936 } 04937 } else { 04938 $errorMsg = "<strong>ERROR:</strong> No class named: " . $parts[0]; 04939 if ($errorMode == 2) { 04940 throw new Exception($errorMsg); 04941 } elseif (!$errorMode) { 04942 debug($errorMsg, 't3lib_div::callUserFunction'); 04943 } 04944 } 04945 } else { // Function 04946 if (function_exists($funcRef)) { 04947 $content = call_user_func_array($funcRef, array(&$params, &$ref)); 04948 } else { 04949 $errorMsg = "<strong>ERROR:</strong> No function named: " . $funcRef; 04950 if ($errorMode == 2) { 04951 throw new Exception($errorMsg); 04952 } elseif (!$errorMode) { 04953 debug($errorMsg, 't3lib_div::callUserFunction'); 04954 } 04955 } 04956 } 04957 return $content; 04958 } 04959 04960 /** 04961 * Creates and returns reference to a user defined object. 04962 * This function can return an object reference if you like. Just prefix the function call with "&": "$objRef = &t3lib_div::getUserObj('EXT:myext/class.tx_myext_myclass.php:&tx_myext_myclass');". This will work ONLY if you prefix the class name with "&" as well. See description of function arguments. 04963 * Usage: 5 04964 * 04965 * @param string Class reference, '[file-reference":"]["&"]class-name'. You can prefix the class name with "[file-reference]:" and t3lib_div::getFileAbsFileName() will then be used to resolve the filename and subsequently include it by "require_once()" which means you don't have to worry about including the class file either! Example: "EXT:realurl/class.tx_realurl.php:&tx_realurl". Finally; for the class name you can prefix it with "&" and you will reuse the previous instance of the object identified by the full reference string (meaning; if you ask for the same $classRef later in another place in the code you will get a reference to the first created one!). 04966 * @param string Required prefix of class name. By default "tx_" is allowed. 04967 * @param boolean If set, no debug() error message is shown if class/function is not present. 04968 * @return object The instance of the class asked for. Instance is created with t3lib_div::makeInstance 04969 * @see callUserFunction() 04970 */ 04971 public static function getUserObj($classRef, $checkPrefix = 'user_', $silent = FALSE) { 04972 global $TYPO3_CONF_VARS; 04973 // Check persistent object and if found, call directly and exit. 04974 if (is_object($GLOBALS['T3_VAR']['getUserObj'][$classRef])) { 04975 return $GLOBALS['T3_VAR']['getUserObj'][$classRef]; 04976 } else { 04977 04978 // Check file-reference prefix; if found, require_once() the file (should be library of code) 04979 if (strpos($classRef, ':') !== FALSE) { 04980 list($file, $class) = self::revExplode(':', $classRef, 2); 04981 $requireFile = self::getFileAbsFileName($file); 04982 if ($requireFile) { 04983 self::requireOnce($requireFile); 04984 } 04985 } else { 04986 $class = $classRef; 04987 } 04988 04989 // Check for persistent object token, "&" 04990 if (substr($class, 0, 1) == '&') { 04991 $class = substr($class, 1); 04992 $storePersistentObject = TRUE; 04993 } else { 04994 $storePersistentObject = FALSE; 04995 } 04996 04997 // Check prefix is valid: 04998 if ($checkPrefix && 04999 !self::isFirstPartOfStr(trim($class), $checkPrefix) && 05000 !self::isFirstPartOfStr(trim($class), 'tx_') 05001 ) { 05002 if (!$silent) { 05003 debug("Class '" . $class . "' was not prepended with '" . $checkPrefix . "'", 't3lib_div::getUserObj'); 05004 } 05005 return FALSE; 05006 } 05007 05008 // Check if class exists: 05009 if (class_exists($class)) { 05010 $classObj = self::makeInstance($class); 05011 05012 // If persistent object should be created, set reference: 05013 if ($storePersistentObject) { 05014 $GLOBALS['T3_VAR']['getUserObj'][$classRef] = $classObj; 05015 } 05016 05017 return $classObj; 05018 } else { 05019 if (!$silent) { 05020 debug("<strong>ERROR:</strong> No class named: " . $class, 't3lib_div::getUserObj'); 05021 } 05022 } 05023 } 05024 } 05025 05026 /** 05027 * Creates an instance of a class taking into account the class-extensions 05028 * API of TYPO3. USE THIS method instead of the PHP "new" keyword. 05029 * Eg. "$obj = new myclass;" should be "$obj = t3lib_div::makeInstance("myclass")" instead! 05030 * 05031 * You can also pass arguments for a constructor: 05032 * t3lib_div::makeInstance('myClass', $arg1, $arg2, ..., $argN) 05033 * 05034 * @throws InvalidArgumentException if classname is an empty string 05035 * @param string $className 05036 * name of the class to instantiate, must not be empty 05037 * @return object the created instance 05038 */ 05039 public static function makeInstance($className) { 05040 if ($className === '') { 05041 throw new InvalidArgumentException('$classname must not be empty.', 1288965219); 05042 } 05043 05044 // Determine final class name which must be instantiated, this takes XCLASS handling 05045 // into account. Cache in a local array to save some cycles for consecutive calls. 05046 if (!isset(self::$finalClassNameRegister[$className])) { 05047 self::$finalClassNameRegister[$className] = self::getClassName($className); 05048 } 05049 $finalClassName = self::$finalClassNameRegister[$className]; 05050 05051 // Return singleton instance if it is already registered 05052 if (isset(self::$singletonInstances[$finalClassName])) { 05053 return self::$singletonInstances[$finalClassName]; 05054 } 05055 05056 // Return instance if it has been injected by addInstance() 05057 if (isset(self::$nonSingletonInstances[$finalClassName]) 05058 && !empty(self::$nonSingletonInstances[$finalClassName]) 05059 ) { 05060 return array_shift(self::$nonSingletonInstances[$finalClassName]); 05061 } 05062 05063 // Create new instance and call constructor with parameters 05064 if (func_num_args() > 1) { 05065 $constructorArguments = func_get_args(); 05066 array_shift($constructorArguments); 05067 05068 $reflectedClass = new ReflectionClass($finalClassName); 05069 $instance = $reflectedClass->newInstanceArgs($constructorArguments); 05070 } else { 05071 $instance = new $finalClassName; 05072 } 05073 05074 // Register new singleton instance 05075 if ($instance instanceof t3lib_Singleton) { 05076 self::$singletonInstances[$finalClassName] = $instance; 05077 } 05078 05079 return $instance; 05080 } 05081 05082 /** 05083 * Returns the class name for a new instance, taking into account the 05084 * class-extension API. 05085 * 05086 * @param string Base class name to evaluate 05087 * @return string Final class name to instantiate with "new [classname]" 05088 */ 05089 protected function getClassName($className) { 05090 if (class_exists($className)) { 05091 while (class_exists('ux_' . $className, FALSE)) { 05092 $className = 'ux_' . $className; 05093 } 05094 } 05095 05096 return $className; 05097 } 05098 05099 /** 05100 * Sets the instance of a singleton class to be returned by makeInstance. 05101 * 05102 * If this function is called multiple times for the same $className, 05103 * makeInstance will return the last set instance. 05104 * 05105 * Warning: This is a helper method for unit tests. Do not call this directly in production code! 05106 * 05107 * @see makeInstance 05108 * @param string $className 05109 * the name of the class to set, must not be empty 05110 * @param t3lib_Singleton $instance 05111 * the instance to set, must be an instance of $className 05112 * @return void 05113 */ 05114 public static function setSingletonInstance($className, t3lib_Singleton $instance) { 05115 self::checkInstanceClassName($className, $instance); 05116 self::$singletonInstances[$className] = $instance; 05117 } 05118 05119 /** 05120 * Sets the instance of a non-singleton class to be returned by makeInstance. 05121 * 05122 * If this function is called multiple times for the same $className, 05123 * makeInstance will return the instances in the order in which they have 05124 * been added (FIFO). 05125 * 05126 * Warning: This is a helper method for unit tests. Do not call this directly in production code! 05127 * 05128 * @see makeInstance 05129 * @throws InvalidArgumentException if class extends t3lib_Singleton 05130 * @param string $className 05131 * the name of the class to set, must not be empty 05132 * @param object $instance 05133 * the instance to set, must be an instance of $className 05134 * @return void 05135 */ 05136 public static function addInstance($className, $instance) { 05137 self::checkInstanceClassName($className, $instance); 05138 05139 if ($instance instanceof t3lib_Singleton) { 05140 throw new InvalidArgumentException( 05141 '$instance must not be an instance of t3lib_Singleton. ' . 05142 'For setting singletons, please use setSingletonInstance.', 05143 1288969325 05144 ); 05145 } 05146 05147 if (!isset(self::$nonSingletonInstances[$className])) { 05148 self::$nonSingletonInstances[$className] = array(); 05149 } 05150 self::$nonSingletonInstances[$className][] = $instance; 05151 } 05152 05153 /** 05154 * Checks that $className is non-empty and that $instance is an instance of 05155 * $className. 05156 * 05157 * @throws InvalidArgumentException if $className is empty or if $instance is no instance of $className 05158 * @param string $className a class name 05159 * @param object $instance an object 05160 * @return void 05161 */ 05162 protected static function checkInstanceClassName($className, $instance) { 05163 if ($className === '') { 05164 throw new InvalidArgumentException('$className must not be empty.', 1288967479); 05165 } 05166 if (!($instance instanceof $className)) { 05167 throw new InvalidArgumentException( 05168 '$instance must be an instance of ' . $className . ', but actually is an instance of ' . get_class($instance) . '.', 05169 1288967686 05170 ); 05171 } 05172 } 05173 05174 /** 05175 * Purge all instances returned by makeInstance. 05176 * 05177 * This function is most useful when called from tearDown in a testcase 05178 * to drop any instances that have been created by the tests. 05179 * 05180 * Warning: This is a helper method for unit tests. Do not call this directly in production code! 05181 * 05182 * @see makeInstance 05183 * @return void 05184 */ 05185 public static function purgeInstances() { 05186 self::$singletonInstances = array(); 05187 self::$nonSingletonInstances = array(); 05188 } 05189 05190 /** 05191 * Find the best service and check if it works. 05192 * Returns object of the service class. 05193 * 05194 * @param string Type of service (service key). 05195 * @param string Sub type like file extensions or similar. Defined by the service. 05196 * @param mixed List of service keys which should be exluded in the search for a service. Array or comma list. 05197 * @return object The service object or an array with error info's. 05198 * @author René Fritz <r.fritz@colorcube.de> 05199 */ 05200 public static function makeInstanceService($serviceType, $serviceSubType = '', $excludeServiceKeys = array()) { 05201 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS; 05202 05203 $error = FALSE; 05204 05205 if (!is_array($excludeServiceKeys)) { 05206 $excludeServiceKeys = self::trimExplode(',', $excludeServiceKeys, 1); 05207 } 05208 while ($info = t3lib_extMgm::findService($serviceType, $serviceSubType, $excludeServiceKeys)) { 05209 05210 // Check persistent object and if found, call directly and exit. 05211 if (is_object($GLOBALS['T3_VAR']['makeInstanceService'][$info['className']])) { 05212 // reset service and return object 05213 $T3_VAR['makeInstanceService'][$info['className']]->reset(); 05214 return $GLOBALS['T3_VAR']['makeInstanceService'][$info['className']]; 05215 05216 // include file and create object 05217 } else { 05218 $requireFile = self::getFileAbsFileName($info['classFile']); 05219 if (@is_file($requireFile)) { 05220 self::requireOnce($requireFile); 05221 $obj = self::makeInstance($info['className']); 05222 if (is_object($obj)) { 05223 if (!@is_callable(array($obj, 'init'))) { 05224 // use silent logging??? I don't think so. 05225 die ('Broken service:' . t3lib_utility_Debug::viewArray($info)); 05226 } 05227 $obj->info = $info; 05228 if ($obj->init()) { // service available? 05229 05230 // create persistent object 05231 $T3_VAR['makeInstanceService'][$info['className']] = $obj; 05232 05233 // needed to delete temp files 05234 register_shutdown_function(array(&$obj, '__destruct')); 05235 05236 return $obj; // object is passed as reference by function definition 05237 } 05238 $error = $obj->getLastErrorArray(); 05239 unset($obj); 05240 } 05241 } 05242 } 05243 // deactivate the service 05244 t3lib_extMgm::deactivateService($info['serviceType'], $info['serviceKey']); 05245 } 05246 return $error; 05247 } 05248 05249 /** 05250 * Require a class for TYPO3 05251 * Useful to require classes from inside other classes (not global scope). A limited set of global variables are available (see function) 05252 */ 05253 public static function requireOnce($requireFile) { 05254 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS; 05255 05256 require_once ($requireFile); 05257 } 05258 05259 /** 05260 * Requires a class for TYPO3 05261 * Useful to require classes from inside other classes (not global scope). 05262 * A limited set of global variables are available (see function) 05263 * 05264 * @param string $requireFile: Path of the file to be included 05265 * @return void 05266 */ 05267 public static function requireFile($requireFile) { 05268 global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS; 05269 require $requireFile; 05270 } 05271 05272 /** 05273 * Simple substitute for the PHP function mail() which allows you to specify encoding and character set 05274 * The fifth parameter ($encoding) will allow you to specify 'base64' encryption for the output (set $encoding=base64) 05275 * Further the output has the charset set to ISO-8859-1 by default. 05276 * Usage: 4 05277 * 05278 * @param string Email address to send to. (see PHP function mail()) 05279 * @param string Subject line, non-encoded. (see PHP function mail()) 05280 * @param string Message content, non-encoded. (see PHP function mail()) 05281 * @param string Headers, separated by LF 05282 * @param string Encoding type: "base64", "quoted-printable", "8bit". Default value is "quoted-printable". 05283 * @param string Charset used in encoding-headers (only if $encoding is set to a valid value which produces such a header) 05284 * @param boolean If set, the header content will not be encoded. 05285 * @return boolean True if mail was accepted for delivery, false otherwise 05286 */ 05287 public static function plainMailEncoded($email, $subject, $message, $headers = '', $encoding = 'quoted-printable', $charset = '', $dontEncodeHeader = FALSE) { 05288 if (!$charset) { 05289 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] ? $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] : 'ISO-8859-1'; 05290 } 05291 05292 $email = self::normalizeMailAddress($email); 05293 if (!$dontEncodeHeader) { 05294 // Mail headers must be ASCII, therefore we convert the whole header to either base64 or quoted_printable 05295 $newHeaders = array(); 05296 foreach (explode(LF, $headers) as $line) { // Split the header in lines and convert each line separately 05297 $parts = explode(': ', $line, 2); // Field tags must not be encoded 05298 if (count($parts) == 2) { 05299 if (0 == strcasecmp($parts[0], 'from')) { 05300 $parts[1] = self::normalizeMailAddress($parts[1]); 05301 } 05302 $parts[1] = self::encodeHeader($parts[1], $encoding, $charset); 05303 $newHeaders[] = implode(': ', $parts); 05304 } else { 05305 $newHeaders[] = $line; // Should never happen - is such a mail header valid? Anyway, just add the unchanged line... 05306 } 05307 } 05308 $headers = implode(LF, $newHeaders); 05309 unset($newHeaders); 05310 05311 $email = self::encodeHeader($email, $encoding, $charset); // Email address must not be encoded, but it could be appended by a name which should be so (e.g. "Kasper Skårhøj <kasperYYYY@typo3.com>") 05312 $subject = self::encodeHeader($subject, $encoding, $charset); 05313 } 05314 05315 switch ((string) $encoding) { 05316 case 'base64': 05317 $headers = trim($headers) . LF . 05318 'Mime-Version: 1.0' . LF . 05319 'Content-Type: text/plain; charset="' . $charset . '"' . LF . 05320 'Content-Transfer-Encoding: base64'; 05321 05322 $message = trim(chunk_split(base64_encode($message . LF))) . LF; // Adding LF because I think MS outlook 2002 wants it... may be removed later again. 05323 break; 05324 case '8bit': 05325 $headers = trim($headers) . LF . 05326 'Mime-Version: 1.0' . LF . 05327 'Content-Type: text/plain; charset=' . $charset . LF . 05328 'Content-Transfer-Encoding: 8bit'; 05329 break; 05330 case 'quoted-printable': 05331 default: 05332 $headers = trim($headers) . LF . 05333 'Mime-Version: 1.0' . LF . 05334 'Content-Type: text/plain; charset=' . $charset . LF . 05335 'Content-Transfer-Encoding: quoted-printable'; 05336 05337 $message = self::quoted_printable($message); 05338 break; 05339 } 05340 05341 // Headers must be separated by CRLF according to RFC 2822, not just LF. 05342 // But many servers (Gmail, for example) behave incorectly and want only LF. 05343 // So we stick to LF in all cases. 05344 $headers = trim(implode(LF, self::trimExplode(LF, $headers, TRUE))); // Make sure no empty lines are there. 05345 05346 05347 return t3lib_utility_Mail::mail($email, $subject, $message, $headers); 05348 } 05349 05350 /** 05351 * Implementation of quoted-printable encode. 05352 * This functions is buggy. It seems that in the part where the lines are breaked every 76th character, that it fails if the break happens right in a quoted_printable encode character! 05353 * See RFC 1521, section 5.1 Quoted-Printable Content-Transfer-Encoding 05354 * Usage: 2 05355 * 05356 * @param string Content to encode 05357 * @param integer Length of the lines, default is 76 05358 * @return string The QP encoded string 05359 */ 05360 public static function quoted_printable($string, $maxlen = 76) { 05361 // Make sure the string contains only Unix linebreaks 05362 $string = str_replace(CRLF, LF, $string); // Replace Windows breaks (\r\n) 05363 $string = str_replace(CR, LF, $string); // Replace Mac breaks (\r) 05364 05365 $linebreak = LF; // Default line break for Unix systems. 05366 if (TYPO3_OS == 'WIN') { 05367 $linebreak = CRLF; // Line break for Windows. This is needed because PHP on Windows systems send mails via SMTP instead of using sendmail, and thus the linebreak needs to be \r\n. 05368 } 05369 05370 $newString = ''; 05371 $theLines = explode(LF, $string); // Split lines 05372 foreach ($theLines as $val) { 05373 $newVal = ''; 05374 $theValLen = strlen($val); 05375 $len = 0; 05376 for ($index = 0; $index < $theValLen; $index++) { // Walk through each character of this line 05377 $char = substr($val, $index, 1); 05378 $ordVal = ord($char); 05379 if ($len > ($maxlen - 4) || ($len > ($maxlen - 14) && $ordVal == 32)) { 05380 $newVal .= '=' . $linebreak; // Add a line break 05381 $len = 0; // Reset the length counter 05382 } 05383 if (($ordVal >= 33 && $ordVal <= 60) || ($ordVal >= 62 && $ordVal <= 126) || $ordVal == 9 || $ordVal == 32) { 05384 $newVal .= $char; // This character is ok, add it to the message 05385 $len++; 05386 } else { 05387 $newVal .= sprintf('=%02X', $ordVal); // Special character, needs to be encoded 05388 $len += 3; 05389 } 05390 } 05391 $newVal = preg_replace('/' . chr(32) . '$/', '=20', $newVal); // Replaces a possible SPACE-character at the end of a line 05392 $newVal = preg_replace('/' . TAB . '$/', '=09', $newVal); // Replaces a possible TAB-character at the end of a line 05393 $newString .= $newVal . $linebreak; 05394 } 05395 return preg_replace('/' . $linebreak . '$/', '', $newString); // Remove last newline 05396 } 05397 05398 /** 05399 * Encode header lines 05400 * Email headers must be ASCII, therefore they will be encoded to quoted_printable (default) or base64. 05401 * 05402 * @param string Content to encode 05403 * @param string Encoding type: "base64" or "quoted-printable". Default value is "quoted-printable". 05404 * @param string Charset used for encoding 05405 * @return string The encoded string 05406 */ 05407 public static function encodeHeader($line, $enc = 'quoted-printable', $charset = 'iso-8859-1') { 05408 // Avoid problems if "###" is found in $line (would conflict with the placeholder which is used below) 05409 if (strpos($line, '###') !== FALSE) { 05410 return $line; 05411 } 05412 // Check if any non-ASCII characters are found - otherwise encoding is not needed 05413 if (!preg_match('/[^' . chr(32) . '-' . chr(127) . ']/', $line)) { 05414 return $line; 05415 } 05416 // Wrap email addresses in a special marker 05417 $line = preg_replace('/([^ ]+@[^ ]+)/', '###$1###', $line); 05418 05419 $matches = preg_split('/(.?###.+###.?|\(|\))/', $line, -1, PREG_SPLIT_NO_EMPTY); 05420 foreach ($matches as $part) { 05421 $oldPart = $part; 05422 switch ((string) $enc) { 05423 case 'base64': 05424 $part = '=?' . $charset . '?B?' . base64_encode($part) . '?='; 05425 break; 05426 case 'quoted-printable': 05427 default: 05428 $qpValue = self::quoted_printable($part, 1000); 05429 if ($part != $qpValue) { 05430 // Encoded words in the header should not contain non-encoded: 05431 // * spaces. "_" is a shortcut for "=20". See RFC 2047 for details. 05432 // * question mark. See RFC 1342 (http://tools.ietf.org/html/rfc1342) 05433 $search = array(' ', '?'); 05434 $replace = array('_', '=3F'); 05435 $qpValue = str_replace($search, $replace, $qpValue); 05436 $part = '=?' . $charset . '?Q?' . $qpValue . '?='; 05437 } 05438 break; 05439 } 05440 $line = str_replace($oldPart, $part, $line); 05441 } 05442 $line = preg_replace('/###(.+?)###/', '$1', $line); // Remove the wrappers 05443 05444 return $line; 05445 } 05446 05447 /** 05448 * Takes a clear-text message body for a plain text email, finds all 'http://' links and if they are longer than 76 chars they are converted to a shorter URL with a hash parameter. The real parameter is stored in the database and the hash-parameter/URL will be redirected to the real parameter when the link is clicked. 05449 * This function is about preserving long links in messages. 05450 * Usage: 3 05451 * 05452 * @param string Message content 05453 * @param string URL mode; "76" or "all" 05454 * @param string URL of index script (see makeRedirectUrl()) 05455 * @return string Processed message content 05456 * @see makeRedirectUrl() 05457 */ 05458 public static function substUrlsInPlainText($message, $urlmode = '76', $index_script_url = '') { 05459 // Substitute URLs with shorter links: 05460 foreach (array('http', 'https') as $protocol) { 05461 $urlSplit = explode($protocol . '://', $message); 05462 foreach ($urlSplit as $c => &$v) { 05463 if ($c) { 05464 $newParts = preg_split('/\s|[<>"{}|\\\^`()\']/', $v, 2); 05465 $newURL = $protocol . '://' . $newParts[0]; 05466 05467 switch ((string) $urlmode) { 05468 case 'all': 05469 $newURL = self::makeRedirectUrl($newURL, 0, $index_script_url); 05470 break; 05471 case '76': 05472 $newURL = self::makeRedirectUrl($newURL, 76, $index_script_url); 05473 break; 05474 } 05475 $v = $newURL . substr($v, strlen($newParts[0])); 05476 } 05477 } 05478 $message = implode('', $urlSplit); 05479 } 05480 05481 return $message; 05482 } 05483 05484 /** 05485 * Subfunction for substUrlsInPlainText() above. 05486 * Usage: 2 05487 * 05488 * @param string Input URL 05489 * @param integer URL string length limit 05490 * @param string URL of "index script" - the prefix of the "?RDCT=..." parameter. If not supplyed it will default to t3lib_div::getIndpEnv('TYPO3_REQUEST_DIR').'index.php' 05491 * @return string Processed URL 05492 */ 05493 public static function makeRedirectUrl($inUrl, $l = 0, $index_script_url = '') { 05494 if (strlen($inUrl) > $l) { 05495 $md5 = substr(md5($inUrl), 0, 20); 05496 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( 05497 '*', 05498 'cache_md5params', 05499 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params') 05500 ); 05501 if (!$count) { 05502 $insertFields = array( 05503 'md5hash' => $md5, 05504 'tstamp' => $GLOBALS['EXEC_TIME'], 05505 'type' => 2, 05506 'params' => $inUrl 05507 ); 05508 05509 $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_md5params', $insertFields); 05510 } 05511 $inUrl = ($index_script_url ? $index_script_url : self::getIndpEnv('TYPO3_REQUEST_DIR') . 'index.php') . 05512 '?RDCT=' . $md5; 05513 } 05514 05515 return $inUrl; 05516 } 05517 05518 /** 05519 * Function to compensate for FreeType2 96 dpi 05520 * Usage: 21 05521 * 05522 * @param integer Fontsize for freetype function call 05523 * @return integer Compensated fontsize based on $GLOBALS['TYPO3_CONF_VARS']['GFX']['TTFdpi'] 05524 */ 05525 public static function freetypeDpiComp($font_size) { 05526 $dpi = intval($GLOBALS['TYPO3_CONF_VARS']['GFX']['TTFdpi']); 05527 if ($dpi != 72) { 05528 $font_size = $font_size / $dpi * 72; 05529 } 05530 return $font_size; 05531 } 05532 05533 /** 05534 * Initialize the system log. 05535 * 05536 * @return void 05537 * @see sysLog() 05538 */ 05539 public static function initSysLog() { 05540 global $TYPO3_CONF_VARS; 05541 05542 // for CLI logging name is <fqdn-hostname>:<TYPO3-path> 05543 // Note that TYPO3_REQUESTTYPE is not used here as it may not yet be defined 05544 if (defined('TYPO3_cliMode') && TYPO3_cliMode) { 05545 $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getHostname($requestHost = FALSE) . ':' . PATH_site; 05546 } 05547 // for Web logging name is <protocol>://<request-hostame>/<site-path> 05548 else { 05549 $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] = self::getIndpEnv('TYPO3_SITE_URL'); 05550 } 05551 05552 // init custom logging 05553 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) { 05554 $params = array('initLog' => TRUE); 05555 $fakeThis = FALSE; 05556 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) { 05557 self::callUserFunction($hookMethod, $params, $fakeThis); 05558 } 05559 } 05560 05561 // init TYPO3 logging 05562 foreach (explode(';', $TYPO3_CONF_VARS['SYS']['systemLog'], 2) as $log) { 05563 list($type, $destination) = explode(',', $log, 3); 05564 05565 if ($type == 'syslog') { 05566 if (TYPO3_OS == 'WIN') { 05567 $facility = LOG_USER; 05568 } else { 05569 $facility = constant('LOG_' . strtoupper($destination)); 05570 } 05571 openlog($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'], LOG_ODELAY, $facility); 05572 } 05573 } 05574 05575 $TYPO3_CONF_VARS['SYS']['systemLogLevel'] = self::intInRange($TYPO3_CONF_VARS['SYS']['systemLogLevel'], 0, 4); 05576 $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit'] = TRUE; 05577 } 05578 05579 /** 05580 * Logs message to the system log. 05581 * This should be implemented around the source code, including the Core and both frontend and backend, logging serious errors. 05582 * If you want to implement the sysLog in your applications, simply add lines like: 05583 * t3lib_div::sysLog('[write message in English here]', 'extension_key', 'severity'); 05584 * 05585 * @param string Message (in English). 05586 * @param string Extension key (from which extension you are calling the log) or "Core" 05587 * @param integer Severity: 0 is info, 1 is notice, 2 is warning, 3 is error, 4 is fatal error 05588 * @return void 05589 */ 05590 public static function sysLog($msg, $extKey, $severity = 0) { 05591 global $TYPO3_CONF_VARS; 05592 05593 $severity = self::intInRange($severity, 0, 4); 05594 05595 // is message worth logging? 05596 if (intval($TYPO3_CONF_VARS['SYS']['systemLogLevel']) > $severity) { 05597 return; 05598 } 05599 05600 // initialize logging 05601 if (!$TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogInit']) { 05602 self::initSysLog(); 05603 } 05604 05605 // do custom logging 05606 if (isset($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog']) && 05607 is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) { 05608 $params = array('msg' => $msg, 'extKey' => $extKey, 'backTrace' => debug_backtrace(), 'severity' => $severity); 05609 $fakeThis = FALSE; 05610 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) { 05611 self::callUserFunction($hookMethod, $params, $fakeThis); 05612 } 05613 } 05614 05615 // TYPO3 logging enabled? 05616 if (!$TYPO3_CONF_VARS['SYS']['systemLog']) { 05617 return; 05618 } 05619 05620 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy']; 05621 $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm']; 05622 05623 // use all configured logging options 05624 foreach (explode(';', $TYPO3_CONF_VARS['SYS']['systemLog'], 2) as $log) { 05625 list($type, $destination, $level) = explode(',', $log, 4); 05626 05627 // is message worth logging for this log type? 05628 if (intval($level) > $severity) { 05629 continue; 05630 } 05631 05632 $msgLine = ' - ' . $extKey . ': ' . $msg; 05633 05634 // write message to a file 05635 if ($type == 'file') { 05636 $file = fopen($destination, 'a'); 05637 if ($file) { 05638 flock($file, LOCK_EX); // try locking, but ignore if not available (eg. on NFS and FAT) 05639 fwrite($file, date($dateFormat . ' ' . $timeFormat) . $msgLine . LF); 05640 flock($file, LOCK_UN); // release the lock 05641 fclose($file); 05642 self::fixPermissions($destination); 05643 } 05644 } 05645 // send message per mail 05646 elseif ($type == 'mail') { 05647 list($to, $from) = explode('/', $destination); 05648 if (!t3lib_div::validEmail($from)) { 05649 $from = t3lib_utility_Mail::getSystemFrom(); 05650 } 05651 /** @var $mail t3lib_mail_Message */ 05652 $mail = t3lib_div::makeInstance('t3lib_mail_Message'); 05653 $mail->setTo($to) 05654 ->setFrom($from) 05655 ->setSubject('Warning - error in TYPO3 installation') 05656 ->setBody('Host: ' . $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . LF . 05657 'Extension: ' . $extKey . LF . 05658 'Severity: ' . $severity . LF . 05659 LF . $msg 05660 ); 05661 $mail->send(); 05662 } 05663 // use the PHP error log 05664 elseif ($type == 'error_log') { 05665 error_log($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLogHost'] . $msgLine, 0); 05666 } 05667 // use the system log 05668 elseif ($type == 'syslog') { 05669 $priority = array(LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, LOG_CRIT); 05670 syslog($priority[(int) $severity], $msgLine); 05671 } 05672 } 05673 } 05674 05675 /** 05676 * Logs message to the development log. 05677 * This should be implemented around the source code, both frontend and backend, logging everything from the flow through an application, messages, results from comparisons to fatal errors. 05678 * The result is meant to make sense to developers during development or debugging of a site. 05679 * The idea is that this function is only a wrapper for external extensions which can set a hook which will be allowed to handle the logging of the information to any format they might wish and with any kind of filter they would like. 05680 * If you want to implement the devLog in your applications, simply add lines like: 05681 * if (TYPO3_DLOG) t3lib_div::devLog('[write message in english here]', 'extension key'); 05682 * 05683 * @param string Message (in english). 05684 * @param string Extension key (from which extension you are calling the log) 05685 * @param integer Severity: 0 is info, 1 is notice, 2 is warning, 3 is fatal error, -1 is "OK" message 05686 * @param array Additional data you want to pass to the logger. 05687 * @return void 05688 */ 05689 public static function devLog($msg, $extKey, $severity = 0, $dataVar = FALSE) { 05690 global $TYPO3_CONF_VARS; 05691 05692 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'])) { 05693 $params = array('msg' => $msg, 'extKey' => $extKey, 'severity' => $severity, 'dataVar' => $dataVar); 05694 $fakeThis = FALSE; 05695 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['devLog'] as $hookMethod) { 05696 self::callUserFunction($hookMethod, $params, $fakeThis); 05697 } 05698 } 05699 } 05700 05701 /** 05702 * Writes a message to the deprecation log. 05703 * 05704 * @param string Message (in English). 05705 * @return void 05706 */ 05707 public static function deprecationLog($msg) { 05708 if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) { 05709 return; 05710 } 05711 05712 $log = $GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']; 05713 $date = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ': '); 05714 05715 // legacy values (no strict comparison, $log can be boolean, string or int) 05716 if ($log === TRUE || $log == '1') { 05717 $log = 'file'; 05718 } 05719 05720 if (stripos($log, 'file') !== FALSE) { 05721 // write a longer message to the deprecation log 05722 $destination = self::getDeprecationLogFileName(); 05723 $file = @fopen($destination, 'a'); 05724 if ($file) { 05725 flock($file, LOCK_EX); // try locking, but ignore if not available (eg. on NFS and FAT) 05726 @fwrite($file, $date . $msg . LF); 05727 flock($file, LOCK_UN); // release the lock 05728 @fclose($file); 05729 self::fixPermissions($destination); 05730 } 05731 } 05732 05733 if (stripos($log, 'devlog') !== FALSE) { 05734 // copy message also to the developer log 05735 self::devLog($msg, 'Core', self::SYSLOG_SEVERITY_WARNING); 05736 } 05737 05738 // do not use console in login screen 05739 if (stripos($log, 'console') !== FALSE && isset($GLOBALS['BE_USER']->user['uid'])) { 05740 t3lib_utility_Debug::debug($msg, $date, 'Deprecation Log'); 05741 } 05742 } 05743 05744 /** 05745 * Gets the absolute path to the deprecation log file. 05746 * 05747 * @return string absolute path to the deprecation log file 05748 */ 05749 public static function getDeprecationLogFileName() { 05750 return PATH_typo3conf . 05751 'deprecation_' . 05752 self::shortMD5( 05753 PATH_site . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] 05754 ) . 05755 '.log'; 05756 } 05757 05758 /** 05759 * Logs a call to a deprecated function. 05760 * The log message will be taken from the annotation. 05761 * @return void 05762 */ 05763 public static function logDeprecatedFunction() { 05764 if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) { 05765 return; 05766 } 05767 05768 $trail = debug_backtrace(); 05769 05770 if ($trail[1]['type']) { 05771 $function = new ReflectionMethod($trail[1]['class'], $trail[1]['function']); 05772 } else { 05773 $function = new ReflectionFunction($trail[1]['function']); 05774 } 05775 05776 $msg = ''; 05777 if (preg_match('/@deprecated\s+(.*)/', $function->getDocComment(), $match)) { 05778 $msg = $match[1]; 05779 } 05780 05781 // trigger PHP error with a short message: <function> is deprecated (called from <source>, defined in <source>) 05782 $errorMsg = 'Function ' . $trail[1]['function']; 05783 if ($trail[1]['class']) { 05784 $errorMsg .= ' of class ' . $trail[1]['class']; 05785 } 05786 $errorMsg .= ' is deprecated (called from ' . $trail[1]['file'] . '#' . $trail[1]['line'] . ', defined in ' . $function->getFileName() . '#' . $function->getStartLine() . ')'; 05787 05788 // write a longer message to the deprecation log: <function> <annotion> - <trace> (<source>) 05789 $logMsg = $trail[1]['class'] . $trail[1]['type'] . $trail[1]['function']; 05790 $logMsg .= '() - ' . $msg.' - ' . t3lib_utility_Debug::debugTrail(); 05791 $logMsg .= ' (' . substr($function->getFileName(), strlen(PATH_site)) . '#' . $function->getStartLine() . ')'; 05792 self::deprecationLog($logMsg); 05793 } 05794 05795 /** 05796 * Converts a one dimensional array to a one line string which can be used for logging or debugging output 05797 * Example: "loginType: FE; refInfo: Array; HTTP_HOST: www.example.org; REMOTE_ADDR: 192.168.1.5; REMOTE_HOST:; security_level:; showHiddenRecords: 0;" 05798 * 05799 * @param array Data array which should be outputted 05800 * @param mixed List of keys which should be listed in the output string. Pass a comma list or an array. An empty list outputs the whole array. 05801 * @param integer Long string values are shortened to this length. Default: 20 05802 * @return string Output string with key names and their value as string 05803 */ 05804 public static function arrayToLogString(array $arr, $valueList = array(), $valueLength = 20) { 05805 $str = ''; 05806 if (!is_array($valueList)) { 05807 $valueList = self::trimExplode(',', $valueList, 1); 05808 } 05809 $valListCnt = count($valueList); 05810 foreach ($arr as $key => $value) { 05811 if (!$valListCnt || in_array($key, $valueList)) { 05812 $str .= (string) $key . trim(': ' . self::fixed_lgd_cs(str_replace(LF, '|', (string) $value), $valueLength)) . '; '; 05813 } 05814 } 05815 return $str; 05816 } 05817 05818 /** 05819 * Compile the command for running ImageMagick/GraphicsMagick. 05820 * 05821 * @param string Command to be run: identify, convert or combine/composite 05822 * @param string The parameters string 05823 * @param string Override the default path (e.g. used by the install tool) 05824 * @return string Compiled command that deals with IM6 & GraphicsMagick 05825 */ 05826 public static function imageMagickCommand($command, $parameters, $path = '') { 05827 return t3lib_utility_Command::imageMagickCommand($command, $parameters, $path); 05828 } 05829 05830 /** 05831 * Explode a string (normally a list of filenames) with whitespaces by considering quotes in that string. This is mostly needed by the imageMagickCommand function above. 05832 * 05833 * @param string The whole parameters string 05834 * @param boolean If set, the elements of the resulting array are unquoted. 05835 * @return array Exploded parameters 05836 */ 05837 public static function unQuoteFilenames($parameters, $unQuote = FALSE) { 05838 $paramsArr = explode(' ', trim($parameters)); 05839 05840 $quoteActive = -1; // Whenever a quote character (") is found, $quoteActive is set to the element number inside of $params. A value of -1 means that there are not open quotes at the current position. 05841 foreach ($paramsArr as $k => $v) { 05842 if ($quoteActive > -1) { 05843 $paramsArr[$quoteActive] .= ' ' . $v; 05844 unset($paramsArr[$k]); 05845 if (substr($v, -1) === $paramsArr[$quoteActive][0]) { 05846 $quoteActive = -1; 05847 } 05848 } elseif (!trim($v)) { 05849 unset($paramsArr[$k]); // Remove empty elements 05850 05851 } elseif (preg_match('/^(["\'])/', $v) && substr($v, -1) !== $v[0]) { 05852 $quoteActive = $k; 05853 } 05854 } 05855 05856 if ($unQuote) { 05857 foreach ($paramsArr as $key => &$val) { 05858 $val = preg_replace('/(^"|"$)/', '', $val); 05859 $val = preg_replace('/(^\'|\'$)/', '', $val); 05860 05861 } 05862 } 05863 // return reindexed array 05864 return array_values($paramsArr); 05865 } 05866 05867 05868 /** 05869 * Quotes a string for usage as JS parameter. Depends whether the value is 05870 * used in script tags (it doesn't need/must not get htmlspecialchar'ed in 05871 * this case). 05872 * 05873 * @param string $value the string to encode, may be empty 05874 * @param boolean $withinCData 05875 * whether the escaped data is expected to be used as CDATA and thus 05876 * does not need to be htmlspecialchared 05877 * 05878 * @return string the encoded value already quoted (with single quotes), 05879 * will not be empty 05880 */ 05881 static public function quoteJSvalue($value, $withinCData = FALSE) { 05882 $escapedValue = addcslashes( 05883 $value, '\'' . '"' . '\\' . TAB . LF . CR 05884 ); 05885 if (!$withinCData) { 05886 $escapedValue = htmlspecialchars($escapedValue); 05887 } 05888 return '\'' . $escapedValue . '\''; 05889 } 05890 05891 05892 /** 05893 * Ends and cleans all output buffers 05894 * 05895 * @return void 05896 */ 05897 public static function cleanOutputBuffers() { 05898 while (ob_end_clean()) { 05899 ; 05900 } 05901 header('Content-Encoding: None', TRUE); 05902 } 05903 05904 05905 /** 05906 * Ends and flushes all output buffers 05907 * 05908 * @return void 05909 */ 05910 public static function flushOutputBuffers() { 05911 $obContent = ''; 05912 05913 while ($obContent .= ob_get_clean()) { 05914 ; 05915 } 05916 05917 // if previously a "Content-Encoding: whatever" has been set, we have to unset it 05918 if (!headers_sent()) { 05919 $headersList = headers_list(); 05920 foreach ($headersList as $header) { 05921 // split it up at the : 05922 list($key, $value) = self::trimExplode(':', $header, TRUE); 05923 // check if we have a Content-Encoding other than 'None' 05924 if (strtolower($key) === 'content-encoding' && strtolower($value) !== 'none') { 05925 header('Content-Encoding: None'); 05926 break; 05927 } 05928 } 05929 } 05930 echo $obContent; 05931 } 05932 } 05933 05934 ?>
1.8.0