From 19c6d3f4089566e3895f5126d81ca45af97bc303 Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Wed, 18 Nov 2009 21:48:00 +0000 Subject: [PATCH] MDL-20889 importing latest RC of typo3libs - solves use of functions deprecated in PHP 5.3 --- lib/typo3/class.t3lib_cs.php | 158 +- lib/typo3/class.t3lib_div.php | 1057 +++++-- lib/typo3/readme_moodle.txt | 2 +- lib/typo3/unidata/Translit.txt | 81 + lib/typo3/unidata/UnicodeData.txt | 4488 ++++++++++++++++++++++++++++- 5 files changed, 5389 insertions(+), 397 deletions(-) diff --git a/lib/typo3/class.t3lib_cs.php b/lib/typo3/class.t3lib_cs.php index 5c99892956..34adfe1c80 100644 --- a/lib/typo3/class.t3lib_cs.php +++ b/lib/typo3/class.t3lib_cs.php @@ -2,7 +2,7 @@ /*************************************************************** * Copyright notice * -* (c) 2003-2008 Kasper Skaarhoj (kasperYYYY@typo3.com) +* (c) 2003-2009 Kasper Skaarhoj (kasperYYYY@typo3.com) * All rights reserved * * This script is part of the Typo3 project. The Typo3 project is @@ -24,7 +24,7 @@ /** * Class for conversion between charsets. * - * Typo Id: class.t3lib_cs.php 3439 2008-03-16 19:16:51Z flyguide $ + * Id: class.t3lib_cs.php 5844 2009-08-30 10:02:57Z rupi $ * * @author Kasper Skaarhoj * @author Martin Kutschker @@ -111,6 +111,7 @@ * - trim/ltrim/rtrim: the second parameter 'charlist' won't work for characters not contained in 7-bit ASCII * - strpos/strrpos: they return the BYTE position, if you need the CHARACTER position use utf8_strpos/utf8_strrpos * - htmlentities: charset support for UTF-8 only since PHP 4.3.0 + * - preg_*: Support compiled into PHP by default nowadays, but could be unavailable, need to use modifier * * Functions NOT working on UTF-8 strings: * @@ -119,9 +120,7 @@ * - stripos * - substr * - strrev - * - ereg/eregi * - split/spliti - * - preg_* * - ... * */ @@ -172,8 +171,16 @@ class t3lib_cs { 'cp819' => 'iso-8859-1', 'ibm819' => 'iso-8859-1', 'iso-ir-100' => 'iso-8859-1', - 'iso-ir-109' => 'iso-8859-2', + 'iso-ir-101' => 'iso-8859-2', + 'iso-ir-109' => 'iso-8859-3', + 'iso-ir-110' => 'iso-8859-4', + 'iso-ir-144' => 'iso-8859-5', + 'iso-ir-127' => 'iso-8859-6', + 'iso-ir-126' => 'iso-8859-7', + 'iso-ir-138' => 'iso-8859-8', 'iso-ir-148' => 'iso-8859-9', + 'iso-ir-157' => 'iso-8859-10', + 'iso-ir-179' => 'iso-8859-13', 'iso-ir-199' => 'iso-8859-14', 'iso-ir-203' => 'iso-8859-15', 'csisolatin1' => 'iso-8859-1', @@ -244,12 +251,9 @@ class t3lib_cs { 'ucs4' => 'ucs-4', ); - // mapping of iso-639:2 language codes to script names + // mapping of iso-639-1 language codes to script names var $lang_to_script=array( - // iso-639:2 language codes, see: - // http://www.w3.org/WAI/ER/IG/ert/iso639.htm - // http://www.loc.gov/standards/iso639-2/langcodes.html - // http://www.unicode.org/onlinedat/languages.html + // iso-639-1 language codes, see http://www.loc.gov/standards/iso639-2/php/code_list.php 'ar' => 'arabic', 'bg' => 'cyrillic', // Bulgarian 'bs' => 'east_european', // Bosnian @@ -281,6 +285,8 @@ class t3lib_cs { 'lv' => 'west_european', // Latvian/Lettish 'nl' => 'west_european', // Dutch 'no' => 'west_european', // Norwegian + 'nb' => 'west_european', // Norwegian Bokmal + 'nn' => 'west_european', // Norwegian Nynorsk 'pl' => 'east_european', // Polish 'pt' => 'west_european', // Portuguese 'ro' => 'east_european', // Romanian @@ -715,6 +721,10 @@ class t3lib_cs { */ function utf8_decode($str,$charset,$useEntityForNoChar=0) { + if ($charset === 'utf-8') { + return $str; + } + // Charset is case-insensitive. if ($this->initCharset($charset)) { // Parse conv. table if not already... $strLen = strlen($str); @@ -794,8 +804,8 @@ class t3lib_cs { $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES)); // Getting them in iso-8859-1 - but thats ok since this is observed below. } - $token = 'a'.md5(microtime());//token must start with a letter or preg_replace substitution won't work - $parts = explode($token,preg_replace('/(&([#[:alnum:]]*);)/',$token.'\2'.$token,$str)); + $token = md5(microtime()); + $parts = explode($token, preg_replace('/(&([#[:alnum:]]*);)/', $token . '${2}' . $token, $str)); foreach($parts as $k => $v) { if ($k%2) { if (substr($v,0,1)=='#') { // Dec or hex entities: @@ -987,13 +997,13 @@ class t3lib_cs { // Detect type if not done yet: (Done on first real line) // The "whitespaced" type is on the syntax "0x0A 0x000A #LINE FEED" while "ms-token" is like "B9 = U+00B9 : SUPERSCRIPT ONE" - if (!$detectedType) $detectedType = ereg('[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+',$value) ? 'whitespaced' : 'ms-token'; + if (!$detectedType) $detectedType = preg_match('/[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+/',$value) ? 'whitespaced' : 'ms-token'; if ($detectedType=='ms-token') { - list($hexbyte,$utf8) = split('=|:',$value,3); + list($hexbyte, $utf8) = preg_split('/[=:]/', $value, 3); } elseif ($detectedType=='whitespaced') { $regA=array(); - ereg('[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+',$value,$regA); + preg_match('/[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+/',$value,$regA); $hexbyte = $regA[1]; $utf8 = 'U+'.$regA[2]; } @@ -1074,7 +1084,7 @@ class t3lib_cs { while (!feof($fh)) { $line = fgets($fh,4096); // has a lot of info - list($char,$name,$cat,,,$decomp,,,$num,,,,$upper,$lower,$title,) = split(';', rtrim($line)); + list($char,$name,$cat,,,$decomp,,,$num,,,,$upper,$lower,$title,) = explode(';', rtrim($line)); $ord = hexdec($char); if ($ord > 0xFFFF) break; // only process the BMP @@ -1097,7 +1107,7 @@ class t3lib_cs { // accented Latin letters without "official" decomposition $match = array(); - if (ereg('^LATIN (SMALL|CAPITAL) LETTER ([A-Z]) WITH',$name,$match) && !$decomp) { + if (preg_match('/^LATIN (SMALL|CAPITAL) LETTER ([A-Z]) WITH/',$name,$match) && !$decomp) { $c = ord($match[2]); if ($match[1] == 'SMALL') $c += 32; @@ -1106,7 +1116,7 @@ class t3lib_cs { } $match = array(); - if (ereg('(<.*>)? *(.+)',$decomp,$match)) { + if (preg_match('/(<.*>)? *(.+)/',$decomp,$match)) { switch($match[1]) { case '': // add parenthesis as circle replacement, eg (1) $match[2] = '0028 '.$match[2].' 0029'; @@ -1117,7 +1127,7 @@ class t3lib_cs { break; case '': // ignore multi char decompositions that start with a space - if (ereg('^0020 ',$match[2])) continue 2; + if (preg_match('/^0020 /',$match[2])) continue 2; break; // ignore Arabic and vertical layout presentation decomposition @@ -1128,7 +1138,7 @@ class t3lib_cs { case '': continue 2; } - $decomposition["U+$char"] = split(' ',$match[2]); + $decomposition["U+$char"] = explode(' ', $match[2]); } } fclose($fh); @@ -1146,17 +1156,17 @@ class t3lib_cs { if ($cond == '' || $cond{0} == '#') { $utf8_char = $this->UnumberToChar(hexdec($char)); if ($char != $lower) { - $arr = split(' ',$lower); + $arr = explode(' ', $lower); for ($i=0; isset($arr[$i]); $i++) $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); $utf8CaseFolding['toLower'][$utf8_char] = implode('',$arr); } if ($char != $title && $title != $upper) { - $arr = split(' ',$title); + $arr = explode(' ', $title); for ($i=0; isset($arr[$i]); $i++) $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); $utf8CaseFolding['toTitle'][$utf8_char] = implode('',$arr); } if ($char != $upper) { - $arr = split(' ',$upper); + $arr = explode(' ', $upper); for ($i=0; isset($arr[$i]); $i++) $arr[$i] = $this->UnumberToChar(hexdec($arr[$i])); $utf8CaseFolding['toUpper'][$utf8_char] = implode('',$arr); } @@ -1177,7 +1187,7 @@ class t3lib_cs { if ($line{0} != '#' && trim($line) != '') { list($char,$translit) = t3lib_div::trimExplode(';', $line); if (!$translit) $omit["U+$char"] = 1; - $decomposition["U+$char"] = split(' ', $translit); + $decomposition["U+$char"] = explode(' ', $translit); } } @@ -1452,6 +1462,30 @@ class t3lib_cs { return strlen($string); } + /** + * Method to crop strings using the mb_substr function. + * + * @param string The character set + * @param string String to be cropped + * @param integer Crop length (in characters) + * @param string Crop signifier + * @return string The shortened string + * @see mb_strlen(), mb_substr() + */ + protected function cropMbstring($charset, $string, $len, $crop = '') { + if (intval($len) == 0 || mb_strlen($string) < $len) { + return $string; + } + + if ($len > 0) { + $string = mb_substr($string, 0, $len, $charset) . $crop; + } else { + $string = $crop . mb_substr($string, $len, mb_strlen($string, $charset), $charset); + } + + return $string; + } + /** * Truncates a string and pre-/appends a string. * Unit tested by Kasper @@ -1465,6 +1499,10 @@ class t3lib_cs { * @author Martin Kutschker */ function crop($charset,$string,$len,$crop='') { + if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') { + return $this->cropMbstring($charset, $string, $len, $crop); + } + if (intval($len) == 0) return $string; if ($charset == 'utf-8') { @@ -1591,6 +1629,66 @@ class t3lib_cs { } + /** + * converts the language codes that we get from the client (usually HTTP_ACCEPT_LANGUAGE) + * into a TYPO3-readable language code + * @param $languageCodesList list of language codes. something like 'de,en-us;q=0.9,de-de;q=0.7,es-cl;q=0.6,en;q=0.4,es;q=0.3,zh;q=0.1' + * see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 + * @return string a preferred language that TYPO3 supports, or "default" if none found + * @author Benjamin Mack (benni.typo3.org) + */ + public function getPreferredClientLanguage($languageCodesList) { + $allLanguageCodes = array(); + $selectedLanguage = 'default'; + + // get all languages where TYPO3 code is the same as the ISO code + foreach ($this->charSetArray as $typo3Lang => $charSet) { + $allLanguageCodes[$typo3Lang] = $typo3Lang; + } + + // get all languages where TYPO3 code differs from ISO code + // or needs the country part + // the iso codes will here overwrite the default typo3 language in the key + foreach ($this->isoArray as $typo3Lang => $isoLang) { + $isoLang = join('-', explode('_', $isoLang)); + $allLanguageCodes[$typo3Lang] = $isoLang; + } + + // move the iso codes to the (because we're comparing the keys with "isset" later on) + $allLanguageCodes = array_flip($allLanguageCodes); + + + $preferredLanguages = t3lib_div::trimExplode(',', $languageCodesList); + // order the preferred languages after they key + $sortedPreferredLanguages = array(); + foreach ($preferredLanguages as $preferredLanguage) { + $quality = 1.0; + if (strpos($preferredLanguage, ';q=') !== false) { + list($preferredLanguage, $quality) = explode(';q=', $preferredLanguage); + } + $sortedPreferredLanguages[$preferredLanguage] = $quality; + } + + // loop through the languages, with the highest priority first + arsort($sortedPreferredLanguages, SORT_NUMERIC); + foreach ($sortedPreferredLanguages as $preferredLanguage => $quality) { + if (isset($allLanguageCodes[$preferredLanguage])) { + $selectedLanguage = $allLanguageCodes[$preferredLanguage]; + break; + } + + // strip the country code from the end + list($preferredLanguage, $preferredCountry) = explode('-', $preferredLanguage); + if (isset($allLanguageCodes[$preferredLanguage])) { + $selectedLanguage = $allLanguageCodes[$preferredLanguage]; + break; + } + } + if (!$selectedLanguage || $selectedLanguage == 'en') { + $selectedLanguage = 'default'; + } + return $selectedLanguage; + } @@ -1733,7 +1831,7 @@ class t3lib_cs { if ($i <= 0) return ''; // sanity check for ($bc=0, $mbs=ord($str{$i}); $mbs & 0x80; $mbs = $mbs << 1) $bc++; // calculate number of bytes if ($bc+$i > $len) return substr($str,0,$i); - // fallthru: multibyte char fits into length + // fallthru: multibyte char fits into length } return substr($str,0,$len); } @@ -1948,11 +2046,12 @@ class t3lib_cs { } if (!strlen($str{$i})) return $str; // string shorter than supplied length - if ($i>$len) + if ($i>$len) { return substr($str,0,$len-1); // we ended on a first byte - else + } else { return substr($str,0,$len); - } + } + } /** * Returns a part of a string in the EUC charset family. @@ -2107,4 +2206,5 @@ class t3lib_cs { if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_cs.php']) { include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_cs.php']); } -?> + +?> \ No newline at end of file diff --git a/lib/typo3/class.t3lib_div.php b/lib/typo3/class.t3lib_div.php index 831bd90db8..51c6e4a511 100644 --- a/lib/typo3/class.t3lib_div.php +++ b/lib/typo3/class.t3lib_div.php @@ -2,7 +2,7 @@ /*************************************************************** * Copyright notice * -* (c) 1999-2008 Kasper Skaarhoj (kasperYYYY@typo3.com) +* (c) 1999-2009 Kasper Skaarhoj (kasperYYYY@typo3.com) * All rights reserved * * This script is part of the TYPO3 project. The TYPO3 project is @@ -27,7 +27,7 @@ /** * Contains the reknown class "t3lib_div" with general purpose functions * - * Typo Id: class.t3lib_div.php 3797 2008-06-11 06:39:13Z stucki $ + * Id: class.t3lib_div.php 6469 2009-11-17 23:56:35Z benni $ * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj * XHTML compliant * Usage counts are based on search 22/2 2003 through whole source including tslib/ @@ -239,9 +239,6 @@ final class t3lib_div { const SYSLOG_SEVERITY_FATAL = 4; - - - /************************* * * GET/POST Variables @@ -264,7 +261,6 @@ final class t3lib_div { * * @param string GET/POST var to return * @return mixed POST var named $var and if not set, the GET var of the same name. - * @see GPvar() */ public static function _GP($var) { if(empty($var)) return; @@ -276,7 +272,23 @@ final class t3lib_div { } /** - * Returns the global GET array (or value from) normalized to contain un-escaped values. + * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence. + * + * @param string Key (variable name) from GET or POST vars + * @return array Returns the GET vars merged recursively onto the POST vars. + */ + public static function _GPmerged($parameter) { + $postParameter = is_array($_POST[$parameter]) ? $_POST[$parameter] : array(); + $getParameter = is_array($_GET[$parameter]) ? $_GET[$parameter] : array(); + + $mergedParameters = t3lib_div::array_merge_recursive_overrule($getParameter, $postParameter); + t3lib_div::stripSlashesOnArray($mergedParameters); + + return $mergedParameters; + } + + /** + * Returns the global $_GET array (or value from) normalized to contain un-escaped values. * ALWAYS use this API function to acquire the GET variables! * Usage: 27 * @@ -293,8 +305,8 @@ final class t3lib_div { } /** - * Returns the global POST array (or value from) normalized to contain un-escaped values. - * ALWAYS use this API function to acquire the POST variables! + * Returns the global $_POST array (or value from) normalized to contain un-escaped values. + * ALWAYS use this API function to acquire the $_POST variables! * Usage: 41 * * @param string Optional pointer to value in POST array (basically name of POST var) @@ -333,18 +345,19 @@ final class t3lib_div { } /** - * GET/POST variable - * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order) + * Returns the value of incoming data from globals variable $_POST or $_GET, with priority to $_POST (that is equalent to 'GP' order). * Strips slashes of string-outputs, but not arrays UNLESS $strip is set. If $strip is set all output will have escaped characters unescaped. * Usage: 2 * * @param string GET/POST var to return * @param boolean If set, values are stripped of return values that are *arrays!* - string/integer values returned are always strip-slashed() * @return mixed POST var named $var and if not set, the GET var of the same name. - * @deprecated Use t3lib_div::_GP instead (ALWAYS delivers a value with un-escaped values!) + * @deprecated since TYPO3 3.6 - Use t3lib_div::_GP instead (ALWAYS delivers a value with un-escaped values!) * @see _GP() */ public static function GPvar($var,$strip=0) { + self::logDeprecatedFunction(); + if(empty($var)) return; $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var]; if (isset($value) && is_string($value)) { $value = stripslashes($value); } // Originally check '&& get_magic_quotes_gpc() ' but the values of $_GET are always slashed regardless of get_magic_quotes_gpc() because HTTP_POST/GET_VARS are run through addSlashesOnArray in the very beginning of index_ts.php eg. @@ -353,20 +366,18 @@ final class t3lib_div { } /** - * Returns the GET/POST global arrays merged with POST taking precedence. + * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence. * Usage: 1 * * @param string Key (variable name) from GET or POST vars * @return array Returns the GET vars merged recursively onto the POST vars. - * @ignore - * @deprecated + * @deprecated since TYPO3 3.7 - Use t3lib_div::_GPmerged instead + * @see _GP() */ public static function GParrayMerged($var) { - $postA = is_array($_POST[$var]) ? $_POST[$var] : array(); - $getA = is_array($_GET[$var]) ? $_GET[$var] : array(); - $mergedA = t3lib_div::array_merge_recursive_overrule($getA,$postA); - t3lib_div::stripSlashesOnArray($mergedA); - return $mergedA; + self::logDeprecatedFunction(); + + return self::_GPmerged($var); } /** @@ -378,9 +389,9 @@ final class t3lib_div { * @param string Input string * @return string Input string with potential XSS code removed */ - public function removeXSS($string) { + public static function removeXSS($string) { require_once(PATH_typo3.'contrib/RemoveXSS/RemoveXSS.php'); - $string = RemoveXSS::RemoveXSS($string); + $string = RemoveXSS::process($string); return $string; } @@ -401,7 +412,7 @@ final class t3lib_div { /** - * Compressing a GIF file if not already LZW compressed + * Compressing a GIF file if not already LZW compressed. * 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) * * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file @@ -418,7 +429,6 @@ final class t3lib_div { * @param string Filepath * @param string See description of function * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string. - * @internal */ public static function gif_compress($theFile, $type) { $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX']; @@ -440,13 +450,12 @@ final class t3lib_div { } /** - * Converts a png file to gif + * Converts a png file to gif. * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set true. * Usage: 5 * * @param string $theFile the filename with path * @return string new filename - * @internal */ public static function png_to_gif_by_imagemagick($theFile) { if ($GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] @@ -471,7 +480,6 @@ final class t3lib_div { * @param string Filepath of image file * @param boolean If set, then input file is converted to PNG, otherwise to GIF * @return string If the new image file exists, it's filepath is returned - * @internal */ public static function read_png_gif($theFile,$output_png=0) { if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] && @is_file($theFile)) { @@ -511,7 +519,7 @@ final class t3lib_div { *************************/ /** - * Truncate string + * Truncates string. * Returns a new string of max. $chars length. * If the string is longer, it will be truncated and appended with '...'. * Usage: 39 @@ -520,10 +528,12 @@ final class t3lib_div { * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end. * @param string String to append to the output if it is truncated, default is '...' * @return string new string - * @deprecated Works ONLY for single-byte charsets! USE t3lib_div::fixed_lgd_cs() instead + * @deprecated since TYPO3 4.1 - Works ONLY for single-byte charsets! Use t3lib_div::fixed_lgd_cs() instead * @see fixed_lgd_pre() */ public static function fixed_lgd($string,$origChars,$preStr='...') { + self::logDeprecatedFunction(); + $chars = abs($origChars); if ($chars >= 4) { if(strlen($string)>$chars) { @@ -536,37 +546,43 @@ final class t3lib_div { } /** - * Truncate string + * Truncates string. * Returns a new string of max. $chars length. * If the string is longer, it will be truncated and prepended with '...'. - * This works like fixed_lgd, but is truncated in the start of the string instead of the end + * This works like fixed_lgd(), but is truncated in the start of the string instead of the end * Usage: 6 * * @param string string to truncate * @param integer must be an integer of at least 4 * @return string new string - * @deprecated Use either fixed_lgd() or fixed_lgd_cs() (with negative input value for $chars) + * @deprecated since TYPO3 4.1 - Use either fixed_lgd() or fixed_lgd_cs() (with negative input value for $chars) * @see fixed_lgd() */ public static function fixed_lgd_pre($string,$chars) { + self::logDeprecatedFunction(); + return strrev(t3lib_div::fixed_lgd(strrev($string),$chars)); } /** - * Truncates a string with appended/prepended "..." and takes backend character set into consideration - * Use only from backend! + * Truncates a string with appended/prepended "..." and takes current character set into consideration. * Usage: 75 * * @param string string to truncate * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end. - * @return string New string - * @see fixed_lgd() - */ - public static function fixed_lgd_cs($string,$chars) { - if (is_object($GLOBALS['LANG'])) { - return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet,$string,$chars,'...'); + * @param string appendix to the truncated string + * @return string cropped string + */ + public static function fixed_lgd_cs($string, $chars, $appendString='...') { + if (is_object($GLOBALS['LANG'])) { + return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString); + } elseif (is_object($GLOBALS['TSFE'])) { + $charSet = ($GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet); + return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString); } else { - return t3lib_div::fixed_lgd($string, $chars); + // this case should not happen + $csConvObj = t3lib_div::makeInstance('t3lib_cs'); + return $csConvObj->crop('iso-8859-1', $string, $chars, $appendString); } } @@ -577,10 +593,12 @@ final class t3lib_div { * @param string The string to break up * @param string The string to implode the broken lines with (default/typically \n) * @param integer The line length - * @deprecated Use PHP function wordwrap() + * @deprecated since TYPO3 4.1 - Use PHP function wordwrap() * @return string */ public static function breakTextForEmail($str,$implChar="\n",$charWidth=76) { + self::logDeprecatedFunction(); + $lines = explode(chr(10),$str); $outArr=array(); foreach ($lines as $lStr) { @@ -812,11 +830,7 @@ final class t3lib_div { * @return boolean True if $ip is either of IPv4 or IPv6 format. */ public static function validIP($ip) { - if (strpos($ip, ':') === false) { - return t3lib_div::validIPv4($ip); - } else { - return t3lib_div::validIPv6($ip); - } + return (filter_var($ip, FILTER_VALIDATE_IP) !== false); } /** @@ -828,16 +842,7 @@ final class t3lib_div { * @return boolean True if $ip is of IPv4 format. */ public static function validIPv4($ip) { - $parts = explode('.', $ip); - if (count($parts)==4 && - t3lib_div::testInt($parts[0]) && $parts[0]>=1 && $parts[0]<256 && - t3lib_div::testInt($parts[1]) && $parts[0]>=0 && $parts[0]<256 && - t3lib_div::testInt($parts[2]) && $parts[0]>=0 && $parts[0]<256 && - t3lib_div::testInt($parts[3]) && $parts[0]>=0 && $parts[0]<256) { - return true; - } else { - return false; - } + return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false); } /** @@ -849,19 +854,7 @@ final class t3lib_div { * @return boolean True if $ip is of IPv6 format. */ public static function validIPv6($ip) { - $uppercaseIP = strtoupper($ip); - - $regex = '/^('; - $regex.= '(([\dA-F]{1,4}:){7}[\dA-F]{1,4})|'; - $regex.= '(([\dA-F]{1,4}){1}::([\dA-F]{1,4}:){1,5}[\dA-F]{1,4})|'; - $regex.= '(([\dA-F]{1,4}:){2}:([\dA-F]{1,4}:){1,4}[\dA-F]{1,4})|'; - $regex.= '(([\dA-F]{1,4}:){3}:([\dA-F]{1,4}:){1,3}[\dA-F]{1,4})|'; - $regex.= '(([\dA-F]{1,4}:){4}:([\dA-F]{1,4}:){1,2}[\dA-F]{1,4})|'; - $regex.= '(([\dA-F]{1,4}:){5}:([\dA-F]{1,4}:){0,1}[\dA-F]{1,4})|'; - $regex.= '(::([\dA-F]{1,4}:){0,6}[\dA-F]{1,4})'; - $regex.= ')$/'; - - return preg_match($regex, $uppercaseIP) ? true : false; + return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false); } /** @@ -892,6 +885,17 @@ final class t3lib_div { return false; } + /** + * Checks if a given URL matches the host that currently handles this HTTP request. + * Scheme, hostname and (optional) port of the given URL are compared. + * + * @param string $url: URL to compare with the TYPO3 request host + * @return boolean Whether the URL matches the TYPO3 request host + */ + public static function isOnCurrentHost($url) { + return (stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0); + } + /** * Check for item in list * Check if an item exists in a comma-separated list of items. @@ -1063,7 +1067,7 @@ final class t3lib_div { */ public static function split_fileref($fileref) { $reg = array(); - if ( ereg('(.*/)(.*)$',$fileref,$reg) ) { + if (preg_match('/(.*\/)(.*)$/',$fileref,$reg) ) { $info['path'] = $reg[1]; $info['file'] = $reg[2]; } else { @@ -1071,7 +1075,7 @@ final class t3lib_div { $info['file'] = $fileref; } $reg=''; - if ( ereg('(.*)\.([^\.]*$)',$info['file'],$reg) ) { + if ( preg_match('/(.*)\.([^\.]*$)/',$info['file'],$reg) ) { $info['filebody'] = $reg[1]; $info['fileext'] = strtolower($reg[2]); $info['realFileext'] = $reg[2]; @@ -1147,7 +1151,7 @@ final class t3lib_div { * @return string */ public static function rm_endcomma($string) { - return preg_replace('/,$/','',$string); + return rtrim($string, ','); } /** @@ -1156,10 +1160,12 @@ final class t3lib_div { * * @param string String to process * @return string - * @deprecated Use t3lib_cs::conv_case() instead or for HTML output, wrap your content in ...) + * @deprecated since TYPO3 3.5 - Use t3lib_cs::conv_case() instead or for HTML output, wrap your content in ...) * @ignore */ public static function danish_strtoupper($string) { + self::logDeprecatedFunction(); + $value = strtoupper($string); return strtr($value, '���������������', '���������������'); } @@ -1171,10 +1177,12 @@ final class t3lib_div { * � => ae, � => Oe * * @param string String to convert. - * @deprecated Works only for western europe single-byte charsets! Use t3lib_cs::specCharsToASCII() instead! + * @deprecated since TYPO3 4.1 - Works only for western europe single-byte charsets! Use t3lib_cs::specCharsToASCII() instead! * @return string */ public static function convUmlauts($str) { + self::logDeprecatedFunction(); + $pat = array ( '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/', '/�/' ); $repl = array ( 'ae', 'Ae', 'oe', 'Oe', 'ue', 'Ue', 'ss', 'aa', 'AA', 'oe', 'OE', 'ae', 'AE' ); return preg_replace($pat,$repl,$str); @@ -1287,7 +1295,7 @@ final class t3lib_div { * @see calcParenthesis() */ public static function calcPriority($string) { - $string=preg_replace('/[ ]*/','',$string); // removing all whitespace + $string=preg_replace('/[[:space:]]*/','',$string); // removing all whitespace $string='+'.$string; // Ensuring an operator for the first entrance $qm='\*\/\+-^%'; $regex = '(['.$qm.'])(['.$qm.']?[0-9\.]*)'; @@ -1370,7 +1378,7 @@ final class t3lib_div { * @return string Converted result. */ public static function deHSCentities($str) { - return preg_replace('/&([#A-Za-z0-9]*;)/','&\1',$str); + return preg_replace('/&([#[:alnum:]]*;)/','&\1',$str); } /** @@ -1416,14 +1424,41 @@ final class t3lib_div { * Usage: 5 * * @param string Input string to evaluate - * @return boolean Returns true if the $email address (input string) is valid; Has a "@", domain name with at least one period and only allowed a-z characters. + * @return boolean Returns true if the $email address (input string) is valid */ public static function validEmail($email) { - $email = trim ($email); - if (strpos($email,' ') !== false) { - return false; + return (filter_var($email, FILTER_VALIDATE_EMAIL) !== false); + } + + /** + * Checks if current e-mail sending method does not accept recipient/sender name + * in a call to PHP mail() function. Windows version of mail() and mini_sendmail + * program are known not to process such input correctly and they cause SMTP + * errors. This function will return true if current mail sending method has + * problem with recipient name in recipient/sender argument for mail(). + * + * TODO: 4.3 should have additional configuration variable, which is combined + * by || with the rest in this function. + * + * @return boolean true if mail() does not accept recipient name + */ + public static function isBrokenEmailEnvironment() { + return TYPO3_OS == 'WIN' || (false !== strpos(ini_get('sendmail_path'), 'mini_sendmail')); + } + + /** + * Changes from/to arguments for mail() function to work in any environment. + * + * @param string $address Address to adjust + * @return string Adjusted address + * @see t3lib_::isBrokenEmailEnvironment() + */ + public static function normalizeMailAddress($address) { + if (self::isBrokenEmailEnvironment() && false !== ($pos1 = strrpos($address, '<'))) { + $pos2 = strpos($address, '>', $pos1); + $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1); } - return ereg('^[A-Za-z0-9\._-]+[@][A-Za-z0-9\._-]+[\.].[A-Za-z0-9]+$',$email) ? TRUE : FALSE; + return $address; } /** @@ -1465,7 +1500,93 @@ final class t3lib_div { return strtr((string)$str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } + /** + * Returns a string of highly randomized bytes (over the full 8-bit range). + * + * @copyright Drupal CMS + * @license GNU General Public License version 2 + * @param integer Number of characters (bytes) to return + * @return string Random Bytes + */ + public static function generateRandomBytes($count) { + $output = ''; + // /dev/urandom is available on many *nix systems and is considered + // the best commonly available pseudo-random source. + if (TYPO3_OS != 'WIN' && ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + // fallback if /dev/urandom is not available + if (!isset($output{$count - 1})) { + // We initialize with the somewhat random. + $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] + . microtime() . getmypid(); + while (!isset($output{$count - 1})) { + $randomState = md5(microtime() . mt_rand() . $randomState); + $output .= md5(mt_rand() . $randomState, true); + } + $output = substr($output, strlen($output) - $count, $count); + } + return $output; + } + + /** + * Returns a given string with underscores as UpperCamelCase. + * Example: Converts blog_example to BlogExample + * + * @param string $string: String to be converted to camel case + * @return string UpperCamelCasedWord + */ + public static function underscoredToUpperCamelCase($string) { + $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string)))); + return $upperCamelCase; + } + + /** + * Returns a given string with underscores as lowerCamelCase. + * Example: Converts minimal_value to minimalValue + * + * @param string $string: String to be converted to camel case + * @return string lowerCamelCasedWord + */ + public static function underscoredToLowerCamelCase($string) { + $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string)))); + $lowerCamelCase = self::lcfirst($upperCamelCase); + return $lowerCamelCase; + } + + /** + * Returns a given CamelCasedString as an lowercase string with underscores. + * Example: Converts BlogExample to blog_example, and minimalValue to minimal_value + * + * @param string $string: String to be converted to lowercase underscore + * @return string lowercase_and_underscored_string + */ + public static function camelCaseToLowerCaseUnderscored($string) { + return self::strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string)); + } + + /** + * Converts the first char of a string to lowercase if it is a latin character (A-Z). + * Example: Converts "Hello World" to "hello World" + * + * @param string $string: The string to be used to lowercase the first character + * @return string The string with the first character as lowercase + */ + public static function lcfirst($string) { + return self::strtolower(substr($string, 0, 1)) . substr($string, 1); + } + + /** + * Checks if a given string is a Uniform Resource Locator (URL). + * + * @param string $url: The URL to be validated + * @return boolean Whether the given URL is valid + */ + public static function isValidUrl($url) { + return (filter_var($url, FILTER_VALIDATE_URL) !== false); + } @@ -1504,7 +1625,6 @@ final class t3lib_div { * @param array one-dimensional array of items * @param string item to check for * @return boolean true if $item is in the one-dimensional array $in_array - * @internal */ public static function inArray(array $in_array, $item) { foreach ($in_array as $val) { @@ -1517,15 +1637,19 @@ final class t3lib_div { /** * Explodes a $string delimited by $delim and passes each item in the array through intval(). - * Corresponds to explode(), but with conversion to integers for all values. + * Corresponds to t3lib_div::trimExplode(), but with conversion to integers for all values. * Usage: 76 * * @param string Delimiter string to explode with * @param string The string to explode + * @param boolean If set, all empty values (='') will NOT be set in output + * @param integer If positive, the result will contain a maximum of limit elements, + * if negative, all components except the last -limit are returned, + * if zero (default), the result is not limited at all * @return array Exploded values, all converted to integers */ - public static function intExplode($delim, $string) { - $temp = explode($delim,$string); + public static function intExplode($delim, $string, $onlyNonEmptyValues = false, $limit = 0) { + $temp = self::trimExplode($delim, $string, $onlyNonEmptyValues, $limit); foreach ($temp as &$val) { $val = intval($val); } @@ -1560,31 +1684,41 @@ final class t3lib_div { * * @param string Delimiter string to explode with * @param string The string to explode - * @param boolean If set, all empty values (='') will NOT be set in output + * @param boolean If set, all empty values will be removed in output + * @param integer If positive, the result will contain a maximum of + * $limit elements, if negative, all components except + * the last -$limit are returned, if zero (default), + * the result is not limited at all. Attention though + * that the use of this parameter can slow down this + * function. * @return array Exploded values */ - public static function trimExplode($delim, $string, $onlyNonEmptyValues=0) { - $array = explode($delim, $string); - // for two perfomance reasons the loop is duplicated - // a) avoid check for $onlyNonEmptyValues in foreach loop - // b) avoid unnecessary code when $onlyNonEmptyValues is not set - if ($onlyNonEmptyValues) { - $new_array = array(); - foreach($array as $value) { - $value = trim($value); - if ($value != '') { - $new_array[] = $value; + public static function trimExplode($delim, $string, $removeEmptyValues = false, $limit = 0) { + $explodedValues = explode($delim, $string); + + $result = array_map('trim', $explodedValues); + + if ($removeEmptyValues) { + $temp = array(); + foreach($result as $value) { + if ($value !== '') { + $temp[] = $value; } } - // direct return for perfomance reasons - return $new_array; + $result = $temp; } - foreach($array as &$value) { - $value = trim($value); + if ($limit != 0) { + if ($limit < 0) { + $result = array_slice($result, 0, $limit); + } elseif (count($result) > $limit) { + $lastElements = array_slice($result, $limit - 1); + $result = array_slice($result, 0, $limit - 1); + $result[] = implode($delim, $lastElements); + } } - return $array; + return $result; } /** @@ -1594,9 +1728,11 @@ final class t3lib_div { * @param array Array of values to make unique * @return array * @ignore - * @deprecated Use the PHP function array_unique instead + * @deprecated since TYPO3 3.5 - Use the PHP function array_unique instead */ public static function uniqueArray(array $valueArray) { + self::logDeprecatedFunction(); + return array_unique($valueArray); } @@ -1620,6 +1756,53 @@ final class t3lib_div { return $array; } + /** + * Filters an array to reduce its elements to match the condition. + * The values in $keepItems can be optionally evaluated by a custom callback function. + * + * Example (arguments used to call this function): + * $array = array( + * array('aa' => array('first', 'second'), + * array('bb' => array('third', 'fourth'), + * array('cc' => array('fifth', 'sixth'), + * ); + * $keepItems = array('third'); + * $getValueFunc = create_function('$value', 'return $value[0];'); + * + * Returns: + * array( + * array('bb' => array('third', 'fourth'), + * ) + * + * @param array $array: The initial array to be filtered/reduced + * @param mixed $keepItems: The items which are allowed/kept in the array - accepts array or csv string + * @param string $getValueFunc: (optional) Unique function name set by create_function() used to get the value to keep + * @return array The filtered/reduced array with the kept items + */ + public static function keepItemsInArray(array $array, $keepItems, $getValueFunc=null) { + if ($array) { + // Convert strings to arrays: + if (is_string($keepItems)) { + $keepItems = t3lib_div::trimExplode(',', $keepItems); + } + // create_function() returns a string: + if (!is_string($getValueFunc)) { + $getValueFunc = null; + } + // Do the filtering: + if (is_array($keepItems) && count($keepItems)) { + foreach ($array as $key => $value) { + // Get the value to compare by using the callback function: + $keepValue = (isset($getValueFunc) ? $getValueFunc($value) : $value); + if (!in_array($keepValue, $keepItems)) { + unset($array[$key]); + } + } + } + } + return $array; + } + /** * Implodes a multidim-array into GET-parameters (eg. ¶m[key][key2]=value2¶m[key][key3]=value3) * Usage: 24 @@ -1632,7 +1815,7 @@ final class t3lib_div { * @return string Imploded result, fx. ¶m[key][key2]=value2¶m[key][key3]=value3 * @see explodeUrl2Array() */ - public static function implodeArrayForUrl($name,array $theArray,$str='',$skipBlank=0,$rawurlencodeParamName=0) { + public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = false, $rawurlencodeParamName = false) { foreach($theArray as $Akey => $AVal) { $thisKeyName = $name ? $name.'['.$Akey.']' : $Akey; if (is_array($AVal)) { @@ -1711,6 +1894,7 @@ final class t3lib_div { } else { $value = addslashes($value); } + unset($value); } reset($theArray); } @@ -1732,6 +1916,7 @@ final class t3lib_div { } else { $value = stripslashes($value); } + unset($value); } reset($theArray); } @@ -1751,7 +1936,26 @@ final class t3lib_div { } /** - * Merges two arrays recursively and "binary safe" (integer keys are overridden as well), overruling similar values in the first array ($arr0) with the values of the second array ($arr1) + * Rename Array keys with a given mapping table + * @param array Array by reference which should be remapped + * @param array Array with remap information, array/$oldKey => $newKey) + */ + function remapArrayKeys(&$array, $mappingTable) { + if (is_array($mappingTable)) { + foreach ($mappingTable as $old => $new) { + if ($new && isset($array[$old])) { + $array[$new] = $array[$old]; + unset ($array[$old]); + } + } + } + } + + + /** + * Merges two arrays recursively and "binary safe" (integer keys are + * overridden as well), overruling similar values in the first array + * ($arr0) with the values of the second array ($arr1) * In case of identical keys, ie. keeping the values of the second. * Usage: 0 * @@ -1816,19 +2020,18 @@ final class t3lib_div { return $str; } - /** - * Creates recursively a JSON literal from a mulidimensional associative array. - * Uses Services_JSON (http://mike.teczno.com/JSON/doc/) + /** + * Creates recursively a JSON literal from a multidimensional associative array. + * Uses native function of PHP >= 5.2.0 * * @param array $jsonArray: The array to be transformed to JSON * @return string JSON string + * @deprecated since TYPO3 4.3, use PHP native function json_encode() instead, will be removed in TYPO3 4.5 */ public static function array2json(array $jsonArray) { - if (!$GLOBALS['JSON']) { - require_once(PATH_typo3.'contrib/json/json.php'); - $GLOBALS['JSON'] = t3lib_div::makeInstance('Services_JSON'); - } - return $GLOBALS['JSON']->encode($jsonArray); + self::logDeprecatedFunction(); + + return json_encode($jsonArray); } /** @@ -1914,12 +2117,11 @@ final class t3lib_div { * * @param string HTML-tag string (or attributes only) * @return array Array with the attribute values. - * @internal */ public static function split_tag_attributes($tag) { - $tag_tmp = trim(preg_replace ('/^<[^[:space:]]*/i','',trim($tag))); + $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/','',trim($tag))); // Removes any > in the end of the string - $tag_tmp = trim(preg_replace ('/>$/i','',$tag_tmp)); + $tag_tmp = trim(rtrim($tag_tmp, '>')); $value = array(); while (strcmp($tag_tmp,'')) { // Compared with empty string instead , 030102 @@ -1933,7 +2135,7 @@ final class t3lib_div { $tag_tmp = trim(substr($tag_tmp,1)); // Removes = chars. } else { // There are '' around the value. We look for the next ' ' or '>' - $reg = split('[[:space:]=]',$tag_tmp,2); + $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2); $value[] = trim($reg[0]); $tag_tmp = trim(substr($tag_tmp,strlen($reg[0]),1).$reg[1]); } @@ -1973,10 +2175,12 @@ final class t3lib_div { * @param boolean See implodeAttributes() * @param boolean See implodeAttributes() * @return string See implodeAttributes() - * @deprecated Name was changed into implodeAttributes + * @deprecated since TYPO3 3.7 - Name was changed into implodeAttributes * @see implodeAttributes() */ public static function implodeParams(array $arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE) { + self::logDeprecatedFunction(); + return t3lib_div::implodeAttributes($arr,$xhtmlSafe,$dontOmitBlankAttribs); } @@ -2256,15 +2460,51 @@ final class t3lib_div { /** * Converts an XML string to a PHP array. * This is the reverse function of array2xml() + * This is a wrapper for xml2arrayProcess that adds a two-level cache * Usage: 17 * * @param string XML content to convert into an array * @param string The tag-prefix resolve, eg. a namespace like "T3:" * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content. - * @see array2xml() + * @see array2xml(),xml2arrayProcess() + * @author Fabrizio Branca (added caching) */ public static function xml2array($string,$NSprefix='',$reportDocTag=FALSE) { + static $firstLevelCache = array(); + + $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0')); + + // look up in first level cache + if (!empty($firstLevelCache[$identifier])) { + $array = $firstLevelCache[$identifier]; + } else { + // look up in second level cache + $cacheContent = t3lib_pageSelect::getHash($identifier, 0); + $array = unserialize($cacheContent); + + if ($array === false) { + $array = self::xml2arrayProcess($string, $NSprefix, $reportDocTag); + t3lib_pageSelect::storeHash($identifier, serialize($array), 'ident_xml2array'); + } + // store content in first level cache + $firstLevelCache[$identifier] = $array; + } + return $array; + } + + /** + * Converts an XML string to a PHP array. + * This is the reverse function of array2xml() + * Usage: 1 + * + * @param string XML content to convert into an array + * @param string The tag-prefix resolve, eg. a namespace like "T3:" + * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array + * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content. + * @see array2xml() + */ + protected function xml2arrayProcess($string,$NSprefix='',$reportDocTag=FALSE) { global $TYPO3_CONF_VARS; // Create parser: @@ -2616,16 +2856,10 @@ final class t3lib_div { ) ); $content = @file_get_contents($url, false, $ctx); - if ($content === false && isset($report)) { - // TODO: Remove this once PHP 5.1 support is dropped completely - if (function_exists('error_get_last')) { - $phpError = error_get_last(); - $report['error'] = $phpError['type']; - $report['message'] = $phpError['message']; - } else { - $report['error'] = -1; - $report['message'] = 'Couldn\'t get URL.'; - } + if ($content === false && isset($report)) { + $phpError = error_get_last(); + $report['error'] = $phpError['type']; + $report['message'] = $phpError['message']; } } else { if (isset($report)) { @@ -2675,10 +2909,12 @@ final class t3lib_div { } /** - * Setting file system mode & group ownership of file + * Sets the file system mode and group ownership of file. * - * @param string Filepath of newly created file - * @return void + * @param string $file + * the path of an existing file, must not be escaped + * + * @return void */ public static function fixPermissions($file) { if (@is_file($file) && TYPO3_OS!='WIN') { @@ -2713,7 +2949,7 @@ final class t3lib_div { // Checking if the "subdir" is found: $subdir = substr($fI['dirname'],strlen($dirName)); if ($subdir) { - if (ereg('^[[:alnum:]_]+\/$',$subdir) || ereg('^[[:alnum:]_]+\/[[:alnum:]_]+\/$',$subdir)) { + if (preg_match('/^[[:alnum:]_]+\/$/',$subdir) || preg_match('/^[[:alnum:]_]+\/[[:alnum:]_]+\/$/',$subdir)) { $dirName.= $subdir; if (!@is_dir($dirName)) { t3lib_div::mkdir_deep(PATH_site.'typo3temp/', $subdir); @@ -2818,18 +3054,20 @@ final class t3lib_div { * @return array Returns an array with the directory entries as values. If no path, the return value is nothing. */ public static function get_dirs($path) { - if ($path) { - $d = @dir($path); - if (is_object($d)) { - while($entry=$d->read()) { - if (@is_dir($path.'/'.$entry) && $entry!= '..' && $entry!= '.') { - $filearray[]=$entry; + if ($path) { + if (is_dir($path)) { + $dir = scandir($path); + $dirs = array(); + foreach ($dir as $entry) { + if (is_dir($path . '/' . $entry) && $entry != '..' && $entry != '.') { + $dirs[] = $entry; } } - $d->close(); - } else return 'error'; - return $filearray; + } else { + $dirs = 'error'; + } } + return $dirs; } /** @@ -2848,7 +3086,7 @@ final class t3lib_div { // Initialize variabels: $filearray = array(); $sortarray = array(); - $path = preg_replace('#\/$#','',$path); + $path = rtrim($path, '/'); // Find files+directories: if (@is_dir($path)) { @@ -2988,9 +3226,77 @@ final class t3lib_div { return $path; } + /** + * Returns the maximum upload size for a file that is allowed. Measured in KB. + * This might be handy to find out the real upload limit that is possible for this + * TYPO3 installation. The first parameter can be used to set something that overrides + * the maxFileSize, usually for the TCA values. + * + * @param integer $localLimit: the number of Kilobytes (!) that should be used as + * the initial Limit, otherwise $TYPO3_CONF_VARS['BE']['maxFileSize'] will be used + * @return integer the maximum size of uploads that are allowed (measuered in kilobytes) + */ + public static function getMaxUploadFileSize($localLimit = 0) { + // don't allow more than the global max file size at all + $t3Limit = (intval($localLimit > 0 ? $localLimit : $GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize'])); + // as TYPO3 is handling the file size in KB, multiply by 1024 to get bytes + $t3Limit = $t3Limit * 1024; + // check for PHP restrictions of the maximum size of one of the $_FILES + $phpUploadLimit = self::getBytesFromSizeMeasurement(ini_get('upload_max_filesize')); + // check for PHP restrictions of the maximum $_POST size + $phpPostLimit = self::getBytesFromSizeMeasurement(ini_get('post_max_size')); + // if the total amount of post data is smaller (!) than the upload_max_filesize directive, + // then this is the real limit in PHP + $phpUploadLimit = ($phpPostLimit < $phpUploadLimit ? $phpPostLimit : $phpUploadLimit); + // is the allowed PHP limit (upload_max_filesize) lower than the TYPO3 limit?, also: revert back to KB + return floor($phpUploadLimit < $t3Limit ? $phpUploadLimit : $t3Limit) / 1024; + } + /** + * Gets the bytes value from a measurement string like "100k". + * + * @param string $measurement: The measurement (e.g. "100k") + * @return integer The bytes value (e.g. 102400) + */ + public static function getBytesFromSizeMeasurement($measurement) { + if (stripos($measurement, 'G')) { + $bytes = intval($measurement) * 1024 * 1024 * 1024; + } else if (stripos($measurement, 'M')) { + $bytes = intval($measurement) * 1024 * 1024; + } else if (stripos($measurement, 'K')) { + $bytes = intval($measurement) * 1024; + } else { + $bytes = intval($measurement); + } + return $bytes; + } + + /** + * Retrieves the maximum path length that is valid in the current environment. + * + * @return integer The maximum available path length + * @author Ingo Renner + */ + public static function getMaximumPathLength() { + $maximumPathLength = 0; + + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + // rough assumptions + if (TYPO3_OS == 'WIN') { + // WIN is usually 255, Vista 260, although NTFS can hold about 2k + $maximumPathLength = 255; + } else { + $maximumPathLength = 2048; + } + } else { + // precise information is available since PHP 5.3 + $maximumPathLength = PHP_MAXPATHLEN; + } + + return $maximumPathLength; + } @@ -3016,7 +3322,6 @@ final class t3lib_div { * @param string String to show ASCII value for * @param integer Number of characters to show * @return string The string with ASCII values in separated by a space char. - * @internal */ public static function debug_ordvalue($string,$characters=100) { if(strlen($string) < $characters) $characters = strlen($string); @@ -3067,7 +3372,7 @@ final class t3lib_div { } } $result.= ''; - } else { + } else { $result = ' @@ -3083,7 +3388,6 @@ final class t3lib_div { * * @param mixed Array to print visually (in a table). * @return void - * @internal * @see view_array() */ public static function print_array($array_in) { @@ -3254,19 +3558,21 @@ final class t3lib_div { * @param array Array of GET parameters to include * @return string */ - public static function linkThisScript(array $getParams=array()) { + public static function linkThisScript(array $getParams = array()) { $parts = t3lib_div::getIndpEnv('SCRIPT_NAME'); $params = t3lib_div::_GET(); - foreach($getParams as $k => $v) { - if (strcmp($v,'')) { - $params[$k]=$v; - } else unset($params[$k]); + foreach ($getParams as $key => $value) { + if ($value !== '') { + $params[$key] = $value; + } else { + unset($params[$key]); + } } - $pString = t3lib_div::implodeArrayForUrl('',$params); + $pString = t3lib_div::implodeArrayForUrl('', $params); - return $pString ? $parts.'?'.preg_replace('/^&/','',$pString) : $parts; + return $pString ? $parts . '?' . preg_replace('/^&/', '', $pString) : $parts; } /** @@ -3349,6 +3655,7 @@ final class t3lib_div { TYPO3_REQUEST_SCRIPT = [scheme]://[host][:[port]][path_script] TYPO3_REQUEST_DIR = [scheme]://[host][:[port]][path_dir] TYPO3_SITE_URL = [scheme]://[host][:[port]][path_dir] of the TYPO3 website frontend + TYPO3_SITE_PATH = [path_dir] of the TYPO3 website frontend TYPO3_SITE_SCRIPT = [script / Speaking URL] of the TYPO3 website TYPO3_DOCUMENT_ROOT = Absolute path of root of documents: TYPO3_DOCUMENT_ROOT.SCRIPT_NAME = SCRIPT_FILENAME (typically) TYPO3_SSL = Returns TRUE if this session uses SSL/TLS (https) @@ -3370,7 +3677,7 @@ final class t3lib_div { switch ((string)$getEnvName) { case 'SCRIPT_NAME': - $retVal = (php_sapi_name()=='cgi'||php_sapi_name()=='cgi-fcgi')&&($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) ? ($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) : ($_SERVER['ORIG_SCRIPT_NAME']?$_SERVER['ORIG_SCRIPT_NAME']:$_SERVER['SCRIPT_NAME']); + $retVal = (PHP_SAPI=='cgi'||PHP_SAPI=='cgi-fcgi')&&($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) ? ($_SERVER['ORIG_PATH_INFO']?$_SERVER['ORIG_PATH_INFO']:$_SERVER['PATH_INFO']) : ($_SERVER['ORIG_SCRIPT_NAME']?$_SERVER['ORIG_SCRIPT_NAME']:$_SERVER['SCRIPT_NAME']); // add a prefix if TYPO3 is behind a proxy: ext-domain.com => int-server.com/prefix if (t3lib_div::cmpIP($_SERVER['REMOTE_ADDR'], $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'])) { if (t3lib_div::getIndpEnv('TYPO3_SSL') && $GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyPrefixSSL']) { @@ -3381,7 +3688,7 @@ final class t3lib_div { } break; case 'SCRIPT_FILENAME': - $retVal = str_replace('//','/', str_replace('\\','/', (php_sapi_name()=='cgi'||php_sapi_name()=='isapi' ||php_sapi_name()=='cgi-fcgi')&&($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED'])? ($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED']):($_SERVER['ORIG_SCRIPT_FILENAME']?$_SERVER['ORIG_SCRIPT_FILENAME']:$_SERVER['SCRIPT_FILENAME']))); + $retVal = str_replace('//','/', str_replace('\\','/', (PHP_SAPI=='cgi'||PHP_SAPI=='isapi' ||PHP_SAPI=='cgi-fcgi')&&($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED'])? ($_SERVER['ORIG_PATH_TRANSLATED']?$_SERVER['ORIG_PATH_TRANSLATED']:$_SERVER['PATH_TRANSLATED']):($_SERVER['ORIG_SCRIPT_FILENAME']?$_SERVER['ORIG_SCRIPT_FILENAME']:$_SERVER['SCRIPT_FILENAME']))); break; case 'REQUEST_URI': // Typical application of REQUEST_URI is return urls, forms submitting to itself etc. Example: returnUrl='.rawurlencode(t3lib_div::getIndpEnv('REQUEST_URI')) @@ -3389,7 +3696,7 @@ final class t3lib_div { list($v,$n) = explode('|',$GLOBALS['TYPO3_CONF_VARS']['SYS']['requestURIvar']); $retVal = $GLOBALS[$v][$n]; } elseif (!$_SERVER['REQUEST_URI']) { // This is for ISS/CGI which does not have the REQUEST_URI available. - $retVal = '/'.preg_replace('#^/#','',t3lib_div::getIndpEnv('SCRIPT_NAME')). + $retVal = '/'.ltrim(t3lib_div::getIndpEnv('SCRIPT_NAME'), '/'). ($_SERVER['QUERY_STRING']?'?'.$_SERVER['QUERY_STRING']:''); } else { $retVal = $_SERVER['REQUEST_URI']; @@ -3406,9 +3713,9 @@ final class t3lib_div { case 'PATH_INFO': // $_SERVER['PATH_INFO']!=$_SERVER['SCRIPT_NAME'] is necessary because some servers (Windows/CGI) are seen to set PATH_INFO equal to script_name // Further, there must be at least one '/' in the path - else the PATH_INFO value does not make sense. - // IF 'PATH_INFO' never works for our purpose in TYPO3 with CGI-servers, then 'php_sapi_name()=='cgi'' might be a better check. Right now strcmp($_SERVER['PATH_INFO'],t3lib_div::getIndpEnv('SCRIPT_NAME')) will always return false for CGI-versions, but that is only as long as SCRIPT_NAME is set equal to PATH_INFO because of php_sapi_name()=='cgi' (see above) + // 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) // if (strcmp($_SERVER['PATH_INFO'],t3lib_div::getIndpEnv('SCRIPT_NAME')) && count(explode('/',$_SERVER['PATH_INFO']))>1) { - if (php_sapi_name()!='cgi' && php_sapi_name()!='cgi-fcgi') { + if (PHP_SAPI!='cgi' && PHP_SAPI!='cgi-fcgi') { $retVal = $_SERVER['PATH_INFO']; } break; @@ -3518,6 +3825,9 @@ final class t3lib_div { $retVal = $siteUrl; } break; + case 'TYPO3_SITE_PATH': + $retVal = substr(t3lib_div::getIndpEnv('TYPO3_SITE_URL'), strlen(t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST'))); + break; case 'TYPO3_SITE_SCRIPT': $retVal = substr(t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'),strlen(t3lib_div::getIndpEnv('TYPO3_SITE_URL'))); break; @@ -3529,7 +3839,7 @@ final class t3lib_div { if (t3lib_div::cmpIP($_SERVER['REMOTE_ADDR'], $proxySSL)) { $retVal = true; } else { - $retVal = $_SERVER['SSL_SESSION_ID'] || !strcmp($_SERVER['HTTPS'],'on') || !strcmp($_SERVER['HTTPS'],'1') ? true : false; // see http://bugs.typo3.org/view.php?id=3909 + $retVal = $_SERVER['SSL_SESSION_ID'] || !strcasecmp($_SERVER['HTTPS'], 'on') || !strcmp($_SERVER['HTTPS'], '1') ? true : false; // see http://bugs.typo3.org/view.php?id=3909 } break; case '_ARRAY': @@ -3569,15 +3879,12 @@ final class t3lib_div { } /** - * milliseconds - * microtime recalculated to t3lib_div::milliseconds(1/1000 sec) - * Usage: 20 + * Gets the unixtime as milliseconds. * - * @return integer + * @return integer The unixtime as milliseconds */ - public static function milliseconds() { - $p=explode(' ',microtime()); - return round(($p[0]+$p[1])*1000); + public static function milliseconds() { + return round(microtime(true) * 1000); } /** @@ -3596,10 +3903,12 @@ final class t3lib_div { $bInfo['BROWSER']= 'konqu'; } elseif (strpos($useragent,'Opera') !== false) { $bInfo['BROWSER']= 'opera'; - } elseif (preg_match('/MSIE [4567]/', $useragent)) { + } elseif (strpos($useragent, 'MSIE') !== false) { $bInfo['BROWSER']= 'msie'; - } elseif (strpos($useragent,'Mozilla/4') !== false || strpos($useragent,'Mozilla/5') !== false) { + } elseif (strpos($useragent, 'Mozilla') !== false) { $bInfo['BROWSER']='net'; + } elseif (strpos($useragent, 'Flash') !== false) { + $bInfo['BROWSER'] = 'flash'; } if ($bInfo['BROWSER']) { // Browser version @@ -3657,7 +3966,7 @@ final class t3lib_div { if (strpos($host, ' ')) $host = ''; } // we have not found a FQDN yet - if ($host && strpos('.',$host) === FALSE) { + if ($host && strpos($host, '.') === false) { $ip = gethostbyname($host); // we got an IP address if ($ip != $host) { @@ -3785,12 +4094,55 @@ final class t3lib_div { */ public static function verifyFilenameAgainstDenyPattern($filename) { if (strcmp($filename,'') && strcmp($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'],'')) { - $result = eregi($GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'],$filename); + $result = preg_match('/'.$GLOBALS['TYPO3_CONF_VARS']['BE']['fileDenyPattern'].'/i',$filename); if ($result) return false; // so if a matching filename is found, return false; } return true; } + /** + * Checks if a given string is a valid frame URL to be loaded in the + * backend. + * + * @param string $url potential URL to check + * + * @return string either $url if $url is considered to be harmless, or an + * empty string otherwise + */ + public static function sanitizeLocalUrl($url = '') { + $sanitizedUrl = ''; + $decodedUrl = rawurldecode($url); + + if (!empty($url) && self::removeXSS($decodedUrl) === $decodedUrl) { + $testAbsoluteUrl = self::resolveBackPath($decodedUrl); + $testRelativeUrl = self::resolveBackPath( + t3lib_div::dirname(t3lib_div::getIndpEnv('SCRIPT_NAME')) . '/' . $decodedUrl + ); + + // Pass if URL is on the current host: + if (self::isValidUrl($decodedUrl)) { + if (self::isOnCurrentHost($decodedUrl) && strpos($decodedUrl, self::getIndpEnv('TYPO3_SITE_URL')) === 0) { + $sanitizedUrl = $url; + } + // Pass if URL is an absolute file path: + } elseif (self::isAbsPath($decodedUrl) && self::isAllowedAbsPath($decodedUrl)) { + $sanitizedUrl = $url; + // Pass if URL is absolute and below TYPO3 base directory: + } elseif (strpos($testAbsoluteUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && substr($decodedUrl, 0, 1) === '/') { + $sanitizedUrl = $url; + // Pass if URL is relative and below TYPO3 base directory: + } elseif (strpos($testRelativeUrl, self::getIndpEnv('TYPO3_SITE_PATH')) === 0 && substr($decodedUrl, 0, 1) !== '/') { + $sanitizedUrl = $url; + } + } + + if (!empty($url) && empty($sanitizedUrl)) { + self::sysLog('The URL "' . $url . '" is not considered to be local and was denied.', 'Core', self::SYSLOG_SEVERITY_NOTICE); + } + + return $sanitizedUrl; + } + /** * Moves $source file to $destination if uploaded, otherwise try to make a copy * Usage: 4 @@ -3873,7 +4225,6 @@ final class t3lib_div { * @param string List of fields from the record if that is given. * @param integer Length of returned authentication code. * @return string MD5 hash of 8 chars. - * @internal */ public static function stdAuthCode($uid_or_record,$fields='',$codeLength=8) { @@ -3903,7 +4254,7 @@ final class t3lib_div { * * @param string Query-parameters: "&xxx=yyy&zzz=uuu" * @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 - * @see tslib_fe::makeCacheHash(), tslib_cObj::typoLink() + * @see tslib_fe::makeCacheHash(), tslib_cObj::typoLink(), t3lib_div::calculateCHash() */ public static function cHashParams($addQueryParams) { $params = explode('&',substr($addQueryParams,1)); // Splitting parameters up @@ -3938,6 +4289,30 @@ final class t3lib_div { return $pA; } + /** + * Returns the cHash based on provided query parameters and added values from internal call + * + * @param string Query-parameters: "&xxx=yyy&zzz=uuu" + * @return string Hash of all the values + * @see t3lib_div::cHashParams(), t3lib_div::calculateCHash() + */ + public static function generateCHash($addQueryParams) { + $cHashParams = t3lib_div::cHashParams($addQueryParams); + $cHash = t3lib_div::calculateCHash($cHashParams); + return $cHash; + } + + /** + * Calculates the cHash based on the provided parameters + * + * @param array Array of key-value pairs + * @return string Hash of all the values + */ + public static function calculateCHash($params) { + $cHash = md5(serialize($params)); + return $cHash; + } + /** * Responds on input localization setting value whether the page it comes from should be hidden if no translation exists or not. * @@ -3958,10 +4333,13 @@ final class t3lib_div { * @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. * @param string Language key * @param string Character set (option); if not set, determined by the language key - * @return array Value of $LOCAL_LANG found in the included file. If that array is found it's returned. Otherwise an empty array + * @param integer Error mode (when file could not be found): 0 - call debug(), 1 - do nothing, 2 - throw an exception + * @return array Value of $LOCAL_LANG found in the included file. If that array is found it will returned. + * Otherwise an empty array and it is FALSE in error case. */ - public static function readLLfile($fileRef, $langKey, $charset='') { + public static function readLLfile($fileRef, $langKey, $charset = '', $errorMode = 0) { + $result = FALSE; $file = t3lib_div::getFileAbsFileName($fileRef); if ($file) { $baseFile = preg_replace('/\.(php|xml)$/', '', $file); @@ -3977,11 +4355,28 @@ final class t3lib_div { $LOCAL_LANG = array('default'=>$LOCAL_LANG['default'], $langKey=>$LOCAL_LANG[$langKey]); } } } else { - die('File "'.$file.'" not found!'); + $errorMsg = 'File "' . $fileRef. '" not found!'; + if ($errorMode == 2) { + throw new t3lib_exception($errorMsg); + } elseif(!$errorMode) { + debug($errorMsg, 1); + } + $fileNotFound = TRUE; + } + if (is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRef])) { + foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$fileRef] as $overrideFile) { + $languageOverrideFileName = t3lib_div::getFileAbsFileName($overrideFile); + if (@is_file($languageOverrideFileName)) { + $languageOverrideArray = t3lib_div::readLLXMLfile($languageOverrideFileName, $langKey, $charset); + $LOCAL_LANG = t3lib_div::array_merge_recursive_overrule($LOCAL_LANG, $languageOverrideArray); + } + } } } - - return is_array($LOCAL_LANG) ? $LOCAL_LANG : array(); + if ($fileNotFound !== TRUE) { + $result = is_array($LOCAL_LANG) ? $LOCAL_LANG : array(); + } + return $result; } /** @@ -3993,12 +4388,12 @@ final class t3lib_div { * @param string Character set (optional) * @return array LOCAL_LANG array in return. */ - function readLLPHPfile($fileRef, $langKey, $charset='') { + public static function readLLPHPfile($fileRef, $langKey, $charset='') { if (is_object($GLOBALS['LANG'])) { - $csConvObj = &$GLOBALS['LANG']->csConvObj; + $csConvObj = $GLOBALS['LANG']->csConvObj; } elseif (is_object($GLOBALS['TSFE'])) { - $csConvObj = &$GLOBALS['TSFE']->csConvObj; + $csConvObj = $GLOBALS['TSFE']->csConvObj; } else { $csConvObj = t3lib_div::makeInstance('t3lib_cs'); } @@ -4027,7 +4422,8 @@ final class t3lib_div { // Get PHP data include($fileRef); if (!is_array($LOCAL_LANG)) { - die('\''.$fileRef.'\' is no TYPO3 language file)!'); + $fileName = substr($fileRef, strlen(PATH_site)); + die('\'' . $fileName . '\' is no TYPO3 language file)!'); } // converting the default language (English) @@ -4070,9 +4466,9 @@ final class t3lib_div { public static function readLLXMLfile($fileRef, $langKey, $charset='') { if (is_object($GLOBALS['LANG'])) { - $csConvObj = &$GLOBALS['LANG']->csConvObj; + $csConvObj = $GLOBALS['LANG']->csConvObj; } elseif (is_object($GLOBALS['TSFE'])) { - $csConvObj = &$GLOBALS['TSFE']->csConvObj; + $csConvObj = $GLOBALS['TSFE']->csConvObj; } else { $csConvObj = t3lib_div::makeInstance('t3lib_cs'); } @@ -4103,7 +4499,8 @@ final class t3lib_div { $xmlString = t3lib_div::getUrl($fileRef); $xmlContent = t3lib_div::xml2array($xmlString); if (!is_array($xmlContent)) { - die('The file "'.$fileRef.'" is no TYPO3 language file!'); + $fileName = substr($fileRef, strlen(PATH_site)); + die('The file "' . $fileName . '" is no TYPO3 language file!'); } // Set default LOCAL_LANG array content: @@ -4117,6 +4514,7 @@ final class t3lib_div { foreach ($LOCAL_LANG['default'] as &$labelValue) { $labelValue = $csConvObj->utf8_decode($labelValue, $targetCharset); } + unset($labelValue); } // converting other languages to their "native" charsets @@ -4168,7 +4566,8 @@ final class t3lib_div { $local_xmlString = t3lib_div::getUrl($localized_file); $local_xmlContent = t3lib_div::xml2array($local_xmlString); if (!is_array($local_xmlContent)) { - die('The file "'.$localized_file.'" is no TYPO3 language file!'); + $fileName = substr($localized_file, strlen(PATH_site)); + die('The file "' . $fileName . '" is no TYPO3 language file!'); } $LOCAL_LANG[$langKey] = is_array($local_xmlContent['data'][$langKey]) ? $local_xmlContent['data'][$langKey] : array(); @@ -4182,7 +4581,9 @@ final class t3lib_div { // Cache the content now: $serContent = array('extlang'=>$langKey, 'origFile'=>$hashSource, 'EXT_DATA'=>$LOCAL_LANG[$langKey]); $res = t3lib_div::writeFileToTypo3tempDir($cacheFileName, serialize($serContent)); - if ($res) die('ERROR: '.$res); + if ($res) { + die('ERROR: '.$res); + } } else { // Get content from cache: $serContent = unserialize(t3lib_div::getUrl($cacheFileName)); @@ -4396,11 +4797,11 @@ final class t3lib_div { // Get/Create object of class: if ($storePersistentObject) { // Get reference to current instance of class: if (!is_object($GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]])) { - $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = &t3lib_div::makeInstance($parts[0]); + $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]] = t3lib_div::makeInstance($parts[0]); } - $classObj = &$GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]]; + $classObj = $GLOBALS['T3_VAR']['callUserFunction_classPool'][$parts[0]]; } else { // Create new object: - $classObj = &t3lib_div::makeInstance($parts[0]); + $classObj = t3lib_div::makeInstance($parts[0]); } if (method_exists($classObj, $parts[1])) { @@ -4435,7 +4836,7 @@ final class t3lib_div { } } else { // Function if (function_exists($funcRef)) { - $content = call_user_func_array($funcRef, array(&$params, &$ref)); + $content = call_user_func_array($funcRef, array(&$params, &$ref)); } else { $errorMsg = "ERROR: No function named: ".$funcRef; if ($errorMode == 2) { @@ -4459,7 +4860,7 @@ final class t3lib_div { * @return object The instance of the class asked for. Instance is created with t3lib_div::makeInstance * @see callUserFunction() */ - public static function &getUserObj($classRef,$checkPrefix='user_',$silent=0) { + public static function getUserObj($classRef, $checkPrefix='user_', $silent=false) { global $TYPO3_CONF_VARS; // Check persistent object and if found, call directly and exit. if (is_object($GLOBALS['T3_VAR']['getUserObj'][$classRef])) { @@ -4494,11 +4895,11 @@ final class t3lib_div { // Check if class exists: if (class_exists($class)) { - $classObj = &t3lib_div::makeInstance($class); + $classObj = t3lib_div::makeInstance($class); // If persistent object should be created, set reference: if ($storePersistentObject) { - $GLOBALS['T3_VAR']['getUserObj'][$classRef] = &$classObj; + $GLOBALS['T3_VAR']['getUserObj'][$classRef] = $classObj; } return $classObj; @@ -4509,28 +4910,45 @@ final class t3lib_div { } /** - * Make instance of class - * Takes the class-extensions API of TYPO3 into account - * Please USE THIS instead of the PHP "new" keyword. Eg. "$obj = new myclass;" should be "$obj = t3lib_div::makeInstance("myclass")" instead! - * Usage: 447 + * Creates an instance of a class taking into account the class-extensions + * API of TYPO3. USE THIS method instead of the PHP "new" keyword. + * Eg. "$obj = new myclass;" should be "$obj = t3lib_div::makeInstance("myclass")" instead! + * You can also pass arguments for a constructor: + * t3lib_div::makeInstance('myClass', $arg1, $arg2, ..., $argN) * * @param string Class name to instantiate * @return object A reference to the object */ - public static function &makeInstance($className) { + public static function makeInstance($className) { + // holds references of singletons + static $instances = array(); + + // Get final classname + $className = self::getClassName($className); - // Load class file if not found: - if (!class_exists($className)) { - if (substr($className,0,6)=='t3lib_') { - t3lib_div::requireOnce(PATH_t3lib.'class.'.strtolower($className).'.php'); + if (isset($instances[$className])) { + // it's a singleton, get the existing instance + $instance = $instances[$className]; + } else { + if (func_num_args() > 1) { + // getting the constructor arguments by removing this + // method's first argument (the class name) + $constructorArguments = func_get_args(); + array_shift($constructorArguments); + + $reflectedClass = new ReflectionClass($className); + $instance = $reflectedClass->newInstanceArgs($constructorArguments); + } else { + $instance = new $className; } - } - // Get final classname - $className = t3lib_div::makeInstanceClassName($className); + if ($instance instanceof t3lib_Singleton) { + // it's a singleton, save the instance for later reuse + $instances[$className] = $instance; + } + } - // Return object. - return new $className; + return $instance; } /** @@ -4540,9 +4958,23 @@ final class t3lib_div { * * @param string Base Class name to evaluate * @return string Final class name to instantiate with "new [classname]" + * @deprecated since TYPO3 4.3 - Use t3lib_div::makeInstance('myClass', $arg1, $arg2, ..., $argN) */ public static function makeInstanceClassName($className) { - return class_exists('ux_'.$className) ? t3lib_div::makeInstanceClassName('ux_'.$className) : $className; + self::logDeprecatedFunction(); + + return (class_exists($className) && class_exists('ux_'.$className, false) ? t3lib_div::makeInstanceClassName('ux_' . $className) : $className); + } + + /** + * Returns the class name for a new instance, taking into account the + * class-extension API. + * + * @param string Base class name to evaluate + * @return string Final class name to instantiate with "new [classname]" + */ + protected function getClassName($className) { + return (class_exists($className) && class_exists('ux_' . $className, false) ? self::getClassName('ux_' . $className) : $className); } /** @@ -4555,7 +4987,7 @@ final class t3lib_div { * @return object The service object or an array with error info's. * @author Ren� Fritz */ - public static function &makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array()) { + public static function makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array()) { global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS; $error = FALSE; @@ -4586,7 +5018,7 @@ final class t3lib_div { if ($obj->init()) { // service available? // create persistent object - $T3_VAR['makeInstanceService'][$info['className']] = &$obj; + $T3_VAR['makeInstanceService'][$info['className']] = $obj; // needed to delete temp files register_shutdown_function(array(&$obj, '__destruct')); @@ -4614,6 +5046,19 @@ final class t3lib_div { require_once ($requireFile); } + /** + * Requires a class for TYPO3 + * Useful to require classes from inside other classes (not global scope). + * A limited set of global variables are available (see function) + * + * @param string $requireFile: Path of the file to be included + * @return void + */ + public static function requireFile($requireFile) { + global $T3_SERVICES, $T3_VAR, $TYPO3_CONF_VARS; + require $requireFile; + } + /** * Simple substitute for the PHP function mail() which allows you to specify encoding and character set * The fifth parameter ($encoding) will allow you to specify 'base64' encryption for the output (set $encoding=base64) @@ -4634,12 +5079,16 @@ final class t3lib_div { $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] ? $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'] : 'ISO-8859-1'; } + $email = self::normalizeMailAddress($email); if (!$dontEncodeHeader) { // Mail headers must be ASCII, therefore we convert the whole header to either base64 or quoted_printable $newHeaders=array(); foreach (explode(chr(10),$headers) as $line) { // Split the header in lines and convert each line separately $parts = explode(': ',$line,2); // Field tags must not be encoded if (count($parts)==2) { + if (0 == strcasecmp($parts[0], 'from')) { + $parts[1] = self::normalizeMailAddress($parts[1]); + } $parts[1] = t3lib_div::encodeHeader($parts[1],$encoding,$charset); $newHeaders[] = implode(': ',$parts); } else { @@ -4679,14 +5128,12 @@ final class t3lib_div { break; } - $linebreak = chr(10); // Default line break for Unix systems. - if (TYPO3_OS=='WIN') { - $linebreak = chr(13).chr(10); // 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. - } - - $headers=trim(implode($linebreak,t3lib_div::trimExplode(chr(10),$headers,1))); // Make sure no empty lines are there. + // Headers must be separated by CRLF according to RFC 2822, not just LF. + // But many servers (Gmail, for example) behave incorectly and want only LF. + // So we stick to LF in all cases. + $headers = trim(implode(chr(10), t3lib_div::trimExplode(chr(10), $headers, true))); // Make sure no empty lines are there. - $ret = @mail($email,$subject,$message,$headers); + $ret = @mail($email, $subject, $message, $headers); if (!$ret) { t3lib_div::sysLog('Mail to "'.$email.'" could not be sent (Subject: "'.$subject.'").', 'Core', 3); } @@ -4773,7 +5220,12 @@ final class t3lib_div { default: $qpValue = t3lib_div::quoted_printable($part,1000); if ($part!=$qpValue) { - $qpValue = str_replace(' ','_',$qpValue); // Encoded words in the header should not contain non-encoded spaces. "_" is a shortcut for "=20". See RFC 2047 for details. + // Encoded words in the header should not contain non-encoded: + // * spaces. "_" is a shortcut for "=20". See RFC 2047 for details. + // * question mark. See RFC 1342 (http://tools.ietf.org/html/rfc1342) + $search = array(' ', '?'); + $replace = array('_', '=3F'); + $qpValue = str_replace($search, $replace, $qpValue); $part = '=?'.$charset.'?Q?'.$qpValue.'?='; } break; @@ -4830,16 +5282,19 @@ final class t3lib_div { * @param integer URL string length limit * @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' * @return string Processed URL - * @internal */ public static function makeRedirectUrl($inUrl,$l=0,$index_script_url='') { if (strlen($inUrl)>$l) { $md5 = substr(md5($inUrl),0,20); - $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('md5hash', 'cache_md5params', 'md5hash='.$GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params')); - if (!$GLOBALS['TYPO3_DB']->sql_num_rows($res)) { + $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows( + '*', + 'cache_md5params', + 'md5hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($md5, 'cache_md5params') + ); + if (!$count) { $insertFields = array( 'md5hash' => $md5, - 'tstamp' => time(), + 'tstamp' => $GLOBALS['EXEC_TIME'], 'type' => 2, 'params' => $inUrl ); @@ -4867,7 +5322,7 @@ final class t3lib_div { } /** - * Init system error log. + * Initialize the system log. * * @return void * @see sysLog() @@ -4913,9 +5368,10 @@ final class t3lib_div { } /** - * System error log; This should be implemented around the source code, including the Core and both frontend and backend, logging serious errors. + * Logs message to the system log. + * This should be implemented around the source code, including the Core and both frontend and backend, logging serious errors. * If you want to implement the sysLog in your applications, simply add lines like: - * t3lib_div::sysLog('[write message in English here]', 'extension key'); + * t3lib_div::sysLog('[write message in English here]', 'extension_key', 'severity'); * * @param string Message (in English). * @param string Extension key (from which extension you are calling the log) or "Core" @@ -4937,7 +5393,7 @@ final class t3lib_div { // do custom logging if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'])) { - $params = array('msg'=>$msg, 'extKey'=>$extKey, 'backTrace'=>debug_backtrace()); + $params = array('msg'=>$msg, 'extKey'=>$extKey, 'backTrace'=>debug_backtrace(), 'severity'=>$severity); $fakeThis = FALSE; foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_div.php']['systemLog'] as $hookMethod) { t3lib_div::callUserFunction($hookMethod,$params,$fakeThis); @@ -4993,7 +5449,8 @@ final class t3lib_div { } /** - * Developer log; 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. + * Logs message to the development log. + * 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. * The result is meant to make sense to developers during development or debugging of a site. * 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. * If you want to implement the devLog in your applications, simply add lines like: @@ -5017,6 +5474,78 @@ final class t3lib_div { } } + /** + * Writes a message to the deprecation log. + * + * @param string Message (in English). + * @return void + */ + public static function deprecationLog($msg) { + if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) { + return; + } + + // write a longer message to the deprecation log + $destination = PATH_typo3conf . '/deprecation_' . t3lib_div::shortMD5(PATH_site . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) . '.log'; + $file = @fopen($destination, 'a'); + if ($file) { + $date = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] . ': '); + flock($file, LOCK_EX); // try locking, but ignore if not available (eg. on NFS and FAT) + @fwrite($file, $date.$msg.chr(10)); + flock($file, LOCK_UN); // release the lock + @fclose($file); + } + + // copy message also to the developer log + self::devLog($msg, 'Core', self::SYSLOG_SEVERITY_WARNING); + } + + /** + * Logs a call to a deprecated function. + * The log message will b etaken from the annotation. + * @return void + */ + public static function logDeprecatedFunction() { + if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['enableDeprecationLog']) { + return; + } + + $trail = debug_backtrace(); + + if ($trail[1]['type']) { + $function = new ReflectionMethod($trail[1]['class'], $trail[1]['function']); + } else { + $function = new ReflectionFunction($trail[1]['function']); + } + if (!$msg) { + if (preg_match('/@deprecated\s+(.*)/', $function->getDocComment(), $match)) { + $msg = $match[1]; + } + } + + // trigger PHP error with a short message: is deprecated (called from , defined in ) + $errorMsg = 'Function ' . $trail[1]['function']; + if ($trail[1]['class']) { + $errorMsg .= ' of class ' . $trail[1]['class']; + } + $errorMsg .= ' is deprecated (called from '.$trail[1]['file'] . '#' . $trail[1]['line'] . ', defined in ' . $function->getFileName() . '#' . $function->getStartLine() . ')'; + +// michael@typo3.org: Temporary disabled until error handling is implemented (follows later this week...) +/* + if (defined('E_USER_DEPRECATED')) { + trigger_error($errorMsg, E_USER_DEPRECATED); // PHP 5.3 + } else { + trigger_error($errorMsg, E_USER_NOTICE); // PHP 5.2 + } +*/ + + // write a longer message to the deprecation log: - () + $logMsg = $trail[1]['class'] . $trail[1]['type'] . $trail[1]['function']; + $logMsg .= '() - ' . $msg.' - ' . self::debug_trail(); + $logMsg .= ' (' . substr($function->getFileName(), strlen(PATH_site)) . '#' . $function->getStartLine() . ')'; + self::deprecationLog($logMsg); + } + /** * Converts a one dimensional array to a one line string which can be used for logging or debugging output * Example: "loginType: FE; refInfo: Array; HTTP_HOST: www.example.org; REMOTE_ADDR: 192.168.1.5; REMOTE_HOST:; security_level:; showHiddenRecords: 0;" @@ -5034,7 +5563,7 @@ final class t3lib_div { $valListCnt = count($valueList); foreach ($arr as $key => $value) { if (!$valListCnt || in_array($key, $valueList)) { - $str .= (string)$key.trim(': '.t3lib_div::fixed_lgd(str_replace("\n",'|',(string)$value), $valueLength)).'; '; + $str .= (string)$key.trim(': '.t3lib_div::fixed_lgd_cs(str_replace("\n",'|',(string)$value), $valueLength)).'; '; } } return $str; @@ -5071,6 +5600,18 @@ final class t3lib_div { $path .= (($command=='composite') ? $combineScript : $command).$isExt; } + // strip profile information for thumbnails and reduce their size + if ($parameters && $command != 'identify' && $gfxConf['im_useStripProfileByDefault'] && $gfxConf['im_stripProfileCommand'] != '') { + if (strpos($parameters, $gfxConf['im_stripProfileCommand']) === false) { + // Determine whether the strip profile action has be disabled by TypoScript: + if ($parameters !== '-version' && strpos($parameters, '###SkipStripProfile###') === false) { + $parameters = $gfxConf['im_stripProfileCommand'] . ' ' . $parameters; + } else { + $parameters = str_replace('###SkipStripProfile###', '', $parameters); + } + } + } + $cmdLine = $path.' '.$parameters; if($command=='composite' && $switchCompositeParameters) { // Because of some weird incompatibilities between ImageMagick 4 and 6 (plus GraphicsMagick), it is needed to change the parameters order under some preconditions @@ -5103,12 +5644,12 @@ final class t3lib_div { if($quoteActive > -1) { $paramsArr[$quoteActive] .= ' '.$v; unset($paramsArr[$k]); - if(ereg('"$', $v)) { $quoteActive = -1; } + if(preg_match('/"$/', $v)) { $quoteActive = -1; } } elseif(!trim($v)) { unset($paramsArr[$k]); // Remove empty elements - } elseif(ereg('^"', $v)) { + } elseif(preg_match('/^"/', $v)) { $quoteActive = $k; } } @@ -5118,23 +5659,57 @@ final class t3lib_div { $val = preg_replace('/(^"|"$)/','',$val); } } - return $paramsArr; + // return reindexed array + return array_values($paramsArr); + } + + + /** + * Quotes a string for usage as JS parameter. Depends whether the value is + * used in script tags (it doesn't need/must not get htmlspecialchar'ed in + * this case). + * + * @param string $value the string to encode, may be empty + * @param boolean $withinCData + * whether the escaped data is expected to be used as CDATA and thus + * does not need to be htmlspecialchared + * + * @return string the encoded value already quoted (with single quotes), + * will not be empty + */ + static public function quoteJSvalue($value, $withinCData = false) { + $escapedValue = addcslashes( + $value, '\'' . '"' . '\\' . chr(9) . chr(10) . chr(13) + ); + if (!$withinCData) { + $escapedValue = htmlspecialchars($escapedValue); + } + return '\'' . $escapedValue . '\''; } /** - * Quotes a string for usage as JS parameter. Depends wheter the value is used in script tags (it doesn't need/must not get htmlspecialchar'ed in this case) + * Ends and cleans all output buffers * - * @param string The string to encode. - * @param boolean If the values get's used in
'.nl2br(htmlspecialchars((string)$array_in)).'