[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/typo3/ -> class.t3lib_cs.php (source)

   1  <?php
   2  /***************************************************************
   3   *  Copyright notice
   4   *
   5   *  (c) 2003-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
   6   *  All rights reserved
   7   *
   8   *  This script is part of the Typo3 project. The Typo3 project is
   9   *  free software; you can redistribute it and/or modify
  10   *  it under the terms of the GNU General Public License as published by
  11   *  the Free Software Foundation; either version 2 of the License, or
  12   *  (at your option) any later version.
  13   *
  14   *  The GNU General Public License can be found at
  15   *  http://www.gnu.org/copyleft/gpl.html.
  16   *
  17   *  This script is distributed in the hope that it will be useful,
  18   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  19   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20   *  GNU General Public License for more details.
  21   *
  22   *  This copyright notice MUST APPEAR in all copies of the script!
  23   ***************************************************************/
  24  /**
  25   * Class for conversion between charsets.
  26   *
  27   * @author    Kasper Skårhøj <kasperYYYY@typo3.com>
  28   * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
  29   */
  30  
  31  
  32  /**
  33   * Notes on UTF-8
  34   *
  35   * Functions working on UTF-8 strings:
  36   *
  37   * - strchr/strstr
  38   * - strrchr
  39   * - substr_count
  40   * - implode/explode/join
  41   *
  42   * Functions nearly working on UTF-8 strings:
  43   *
  44   * - strlen: returns the length in BYTES, if you need the length in CHARACTERS use utf8_strlen
  45   * - trim/ltrim/rtrim: the second parameter 'charlist' won't work for characters not contained in 7-bit ASCII
  46   * - strpos/strrpos: they return the BYTE position, if you need the CHARACTER position use utf8_strpos/utf8_strrpos
  47   * - htmlentities: charset support for UTF-8 only since PHP 4.3.0
  48   * - preg_*: Support compiled into PHP by default nowadays, but could be unavailable, need to use modifier
  49   *
  50   * Functions NOT working on UTF-8 strings:
  51   *
  52   * - str*cmp
  53   * - stristr
  54   * - stripos
  55   * - substr
  56   * - strrev
  57   * - split/spliti
  58   * - ...
  59   *
  60   */
  61  /**
  62   * Class for conversion between charsets
  63   *
  64   * @author    Kasper Skårhøj <kasperYYYY@typo3.com>
  65   * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
  66   * @package TYPO3
  67   * @subpackage t3lib
  68   */
  69  class t3lib_cs {
  70  
  71      /**
  72       * @var t3lib_l10n_Locales
  73       */
  74      protected $locales;
  75  
  76      var $noCharByteVal = 63; // ASCII Value for chars with no equivalent.
  77  
  78          // This is the array where parsed conversion tables are stored (cached)
  79      var $parsedCharsets = array();
  80  
  81          // An array where case folding data will be stored (cached)
  82      var $caseFolding = array();
  83  
  84          // An array where charset-to-ASCII mappings are stored (cached)
  85      var $toASCII = array();
  86  
  87          // This tells the converter which charsets has two bytes per char:
  88      var $twoByteSets = array(
  89          'ucs-2' => 1, // 2-byte Unicode
  90      );
  91  
  92          // This tells the converter which charsets has four bytes per char:
  93      var $fourByteSets = array(
  94          'ucs-4' => 1, // 4-byte Unicode
  95          'utf-32' => 1, // 4-byte Unicode (limited to the 21-bits of UTF-16)
  96      );
  97  
  98          // This tells the converter which charsets use a scheme like the Extended Unix Code:
  99      var $eucBasedSets = array(
 100          'gb2312' => 1, // Chinese, simplified.
 101          'big5' => 1, // Chinese, traditional.
 102          'euc-kr' => 1, // Korean
 103          'shift_jis' => 1, // Japanese - WARNING: Shift-JIS includes half-width katakana single-bytes characters above 0x80!
 104      );
 105  
 106          // see    http://developer.apple.com/documentation/macos8/TextIntlSvcs/TextEncodingConversionManager/TEC1.5/TEC.b0.html
 107          // http://czyborra.com/charsets/iso8859.html
 108      var $synonyms = array(
 109          'us' => 'ascii',
 110          'us-ascii' => 'ascii',
 111          'cp819' => 'iso-8859-1',
 112          'ibm819' => 'iso-8859-1',
 113          'iso-ir-100' => 'iso-8859-1',
 114          'iso-ir-101' => 'iso-8859-2',
 115          'iso-ir-109' => 'iso-8859-3',
 116          'iso-ir-110' => 'iso-8859-4',
 117          'iso-ir-144' => 'iso-8859-5',
 118          'iso-ir-127' => 'iso-8859-6',
 119          'iso-ir-126' => 'iso-8859-7',
 120          'iso-ir-138' => 'iso-8859-8',
 121          'iso-ir-148' => 'iso-8859-9',
 122          'iso-ir-157' => 'iso-8859-10',
 123          'iso-ir-179' => 'iso-8859-13',
 124          'iso-ir-199' => 'iso-8859-14',
 125          'iso-ir-203' => 'iso-8859-15',
 126          'csisolatin1' => 'iso-8859-1',
 127          'csisolatin2' => 'iso-8859-2',
 128          'csisolatin3' => 'iso-8859-3',
 129          'csisolatin5' => 'iso-8859-9',
 130          'csisolatin8' => 'iso-8859-14',
 131          'csisolatin9' => 'iso-8859-15',
 132          'csisolatingreek' => 'iso-8859-7',
 133          'iso-celtic' => 'iso-8859-14',
 134          'latin1' => 'iso-8859-1',
 135          'latin2' => 'iso-8859-2',
 136          'latin3' => 'iso-8859-3',
 137          'latin5' => 'iso-8859-9',
 138          'latin6' => 'iso-8859-10',
 139          'latin8' => 'iso-8859-14',
 140          'latin9' => 'iso-8859-15',
 141          'l1' => 'iso-8859-1',
 142          'l2' => 'iso-8859-2',
 143          'l3' => 'iso-8859-3',
 144          'l5' => 'iso-8859-9',
 145          'l6' => 'iso-8859-10',
 146          'l8' => 'iso-8859-14',
 147          'l9' => 'iso-8859-15',
 148          'cyrillic' => 'iso-8859-5',
 149          'arabic' => 'iso-8859-6',
 150          'tis-620' => 'iso-8859-11',
 151          'win874' => 'windows-874',
 152          'win1250' => 'windows-1250',
 153          'win1251' => 'windows-1251',
 154          'win1252' => 'windows-1252',
 155          'win1253' => 'windows-1253',
 156          'win1254' => 'windows-1254',
 157          'win1255' => 'windows-1255',
 158          'win1256' => 'windows-1256',
 159          'win1257' => 'windows-1257',
 160          'win1258' => 'windows-1258',
 161          'cp1250' => 'windows-1250',
 162          'cp1251' => 'windows-1251',
 163          'cp1252' => 'windows-1252',
 164          'ms-ee' => 'windows-1250',
 165          'ms-ansi' => 'windows-1252',
 166          'ms-greek' => 'windows-1253',
 167          'ms-turk' => 'windows-1254',
 168          'winbaltrim' => 'windows-1257',
 169          'koi-8ru' => 'koi-8r',
 170          'koi8r' => 'koi-8r',
 171          'cp878' => 'koi-8r',
 172          'mac' => 'macroman',
 173          'macintosh' => 'macroman',
 174          'euc-cn' => 'gb2312',
 175          'x-euc-cn' => 'gb2312',
 176          'euccn' => 'gb2312',
 177          'cp936' => 'gb2312',
 178          'big-5' => 'big5',
 179          'cp950' => 'big5',
 180          'eucjp' => 'euc-jp',
 181          'sjis' => 'shift_jis',
 182          'shift-jis' => 'shift_jis',
 183          'cp932' => 'shift_jis',
 184          'cp949' => 'euc-kr',
 185          'utf7' => 'utf-7',
 186          'utf8' => 'utf-8',
 187          'utf16' => 'utf-16',
 188          'utf32' => 'utf-32',
 189          'utf8' => 'utf-8',
 190          'ucs2' => 'ucs-2',
 191          'ucs4' => 'ucs-4',
 192      );
 193  
 194          // mapping of iso-639-1 language codes to script names
 195      var $lang_to_script = array(
 196              // iso-639-1 language codes, see http://www.loc.gov/standards/iso639-2/php/code_list.php
 197          'af' => 'west_european', //Afrikaans
 198          'ar' => 'arabic',
 199          'bg' => 'cyrillic', // Bulgarian
 200          'bs' => 'east_european', // Bosnian
 201          'cs' => 'east_european', // Czech
 202          'da' => 'west_european', // Danish
 203          'de' => 'west_european', // German
 204          'es' => 'west_european', // Spanish
 205          'et' => 'estonian',
 206          'eo' => 'unicode', // Esperanto
 207          'eu' => 'west_european', // Basque
 208          'fa' => 'arabic', // Persian
 209          'fi' => 'west_european', // Finish
 210          'fo' => 'west_european', // Faroese
 211          'fr' => 'west_european', // French
 212          'ga' => 'west_european', // Irish
 213          'gl' => 'west_european', // Galician
 214          'gr' => 'greek',
 215          'he' => 'hebrew', // Hebrew (since 1998)
 216          'hi' => 'unicode', // Hindi
 217          'hr' => 'east_european', // Croatian
 218          'hu' => 'east_european', // Hungarian
 219          'iw' => 'hebrew', // Hebrew (til 1998)
 220          'is' => 'west_european', // Icelandic
 221          'it' => 'west_european', // Italian
 222          'ja' => 'japanese',
 223          'ka' => 'unicode', // Georgian
 224          'kl' => 'west_european', // Greenlandic
 225          'km' => 'unicode', // Khmer
 226          'ko' => 'korean',
 227          'lt' => 'lithuanian',
 228          'lv' => 'west_european', // Latvian/Lettish
 229          'nl' => 'west_european', // Dutch
 230          'no' => 'west_european', // Norwegian
 231          'nb' => 'west_european', // Norwegian Bokmal
 232          'nn' => 'west_european', // Norwegian Nynorsk
 233          'pl' => 'east_european', // Polish
 234          'pt' => 'west_european', // Portuguese
 235          'ro' => 'east_european', // Romanian
 236          'ru' => 'cyrillic', // Russian
 237          'sk' => 'east_european', // Slovak
 238          'sl' => 'east_european', // Slovenian
 239          'sr' => 'cyrillic', // Serbian
 240          'sv' => 'west_european', // Swedish
 241          'sq' => 'albanian', // Albanian
 242          'th' => 'thai',
 243          'uk' => 'cyrillic', // Ukranian
 244          'vi' => 'vietnamese',
 245          'zh' => 'chinese',
 246              // MS language codes, see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_language_strings.asp
 247              // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceinternational5/html/wce50conLanguageIdentifiersandLocales.asp
 248          'afk'=> 'west_european', // Afrikaans
 249          'ara' => 'arabic',
 250          'bgr' => 'cyrillic', // Bulgarian
 251          'cat' => 'west_european', // Catalan
 252          'chs' => 'simpl_chinese',
 253          'cht' => 'trad_chinese',
 254          'csy' => 'east_european', // Czech
 255          'dan' => 'west_european', // Danisch
 256          'deu' => 'west_european', // German
 257          'dea' => 'west_european', // German (Austrian)
 258          'des' => 'west_european', // German (Swiss)
 259          'ena' => 'west_european', // English (Australian)
 260          'enc' => 'west_european', // English (Canadian)
 261          'eng' => 'west_european', // English
 262          'enz' => 'west_european', // English (New Zealand)
 263          'enu' => 'west_european', // English (United States)
 264          'euq' => 'west_european', // Basque
 265          'fos' => 'west_european', // Faroese
 266          'far' => 'arabic', // Persian
 267          'fin' => 'west_european', // Finish
 268          'fra' => 'west_european', // French
 269          'frb' => 'west_european', // French (Belgian)
 270          'frc' => 'west_european', // French (Canadian)
 271          'frs' => 'west_european', // French (Swiss)
 272          'geo' => 'unicode', // Georgian
 273          'glg' => 'west_european', // Galician
 274          'ell' => 'greek',
 275          'heb' => 'hebrew',
 276          'hin' => 'unicode', // Hindi
 277          'hun' => 'east_european', // Hungarian
 278          'isl' => 'west_euorpean', // Icelandic
 279          'ita' => 'west_european', // Italian
 280          'its' => 'west_european', // Italian (Swiss)
 281          'jpn' => 'japanese',
 282          'khm' => 'unicode', // Khmer
 283          'kor' => 'korean',
 284          'lth' => 'lithuanian',
 285          'lvi' => 'west_european', // Latvian/Lettish
 286          'msl' => 'west_european', // Malay
 287          'nlb' => 'west_european', // Dutch (Belgian)
 288          'nld' => 'west_european', // Dutch
 289          'nor' => 'west_european', // Norwegian (bokmal)
 290          'non' => 'west_european', // Norwegian (nynorsk)
 291          'plk' => 'east_european', // Polish
 292          'ptg' => 'west_european', // Portuguese
 293          'ptb' => 'west_european', // Portuguese (Brazil)
 294          'rom' => 'east_european', // Romanian
 295          'rus' => 'cyrillic', // Russian
 296          'slv' => 'east_european', // Slovenian
 297          'sky' => 'east_european', // Slovak
 298          'srl' => 'east_european', // Serbian (Latin)
 299          'srb' => 'cyrillic', // Serbian (Cyrillic)
 300          'esp' => 'west_european', // Spanish (trad. sort)
 301          'esm' => 'west_european', // Spanish (Mexican)
 302          'esn' => 'west_european', // Spanish (internat. sort)
 303          'sve' => 'west_european', // Swedish
 304          'sqi' => 'albanian', // Albanian
 305          'tha' => 'thai',
 306          'trk' => 'turkish',
 307          'ukr' => 'cyrillic', // Ukrainian
 308              // English language names
 309          'afrikaans' => 'west_european',
 310          'albanian' => 'albanian',
 311          'arabic' => 'arabic',
 312          'basque' => 'west_european',
 313          'bosnian' => 'east_european',
 314          'bulgarian' => 'east_european',
 315          'catalan' => 'west_european',
 316          'croatian' => 'east_european',
 317          'czech' => 'east_european',
 318          'danish' => 'west_european',
 319          'dutch' => 'west_european',
 320          'english' => 'west_european',
 321          'esperanto' => 'unicode',
 322          'estonian' => 'estonian',
 323          'faroese' => 'west_european',
 324          'farsi' => 'arabic',
 325          'finnish' => 'west_european',
 326          'french' => 'west_european',
 327          'galician' => 'west_european',
 328          'georgian' => 'unicode',
 329          'german' => 'west_european',
 330          'greek' => 'greek',
 331          'greenlandic' => 'west_european',
 332          'hebrew' => 'hebrew',
 333          'hindi' => 'unicode',
 334          'hungarian' => 'east_european',
 335          'icelandic' => 'west_european',
 336          'italian' => 'west_european',
 337          'khmer' => 'unicode',
 338          'latvian' => 'west_european',
 339          'lettish' => 'west_european',
 340          'lithuanian' => 'lithuanian',
 341          'malay' => 'west_european',
 342          'norwegian' => 'west_european',
 343          'persian' => 'arabic',
 344          'polish' => 'east_european',
 345          'portuguese' => 'west_european',
 346          'russian' => 'cyrillic',
 347          'romanian' => 'east_european',
 348          'serbian' => 'cyrillic',
 349          'slovak' => 'east_european',
 350          'slovenian' => 'east_european',
 351          'spanish' => 'west_european',
 352          'svedish' => 'west_european',
 353          'that' => 'thai',
 354          'turkish' => 'turkish',
 355          'ukrainian' => 'cyrillic',
 356      );
 357  
 358          // mapping of language (family) names to charsets on Unix
 359      var $script_to_charset_unix = array(
 360          'west_european' => 'iso-8859-1',
 361          'estonian' => 'iso-8859-1',
 362          'east_european' => 'iso-8859-2',
 363          'baltic' => 'iso-8859-4',
 364          'cyrillic' => 'iso-8859-5',
 365          'arabic' => 'iso-8859-6',
 366          'greek' => 'iso-8859-7',
 367          'hebrew' => 'iso-8859-8',
 368          'turkish' => 'iso-8859-9',
 369          'thai' => 'iso-8859-11', // = TIS-620
 370          'lithuanian' => 'iso-8859-13',
 371          'chinese' => 'gb2312', // = euc-cn
 372          'japanese' => 'euc-jp',
 373          'korean' => 'euc-kr',
 374          'simpl_chinese' => 'gb2312',
 375          'trad_chinese' => 'big5',
 376          'vietnamese' => '',
 377          'unicode' => 'utf-8',
 378          'albanian' => 'utf-8'
 379      );
 380  
 381          // mapping of language (family) names to charsets on Windows
 382      var $script_to_charset_windows = array(
 383          'east_european' => 'windows-1250',
 384          'cyrillic' => 'windows-1251',
 385          'west_european' => 'windows-1252',
 386          'greek' => 'windows-1253',
 387          'turkish' => 'windows-1254',
 388          'hebrew' => 'windows-1255',
 389          'arabic' => 'windows-1256',
 390          'baltic' => 'windows-1257',
 391          'estonian' => 'windows-1257',
 392          'lithuanian' => 'windows-1257',
 393          'vietnamese' => 'windows-1258',
 394          'thai' => 'cp874',
 395          'korean' => 'cp949',
 396          'chinese' => 'gb2312',
 397          'japanese' => 'shift_jis',
 398          'simpl_chinese' => 'gb2312',
 399          'trad_chinese' => 'big5',
 400          'albanian' => 'windows-1250',
 401          'unicode' => 'utf-8'
 402      );
 403  
 404          // mapping of locale names to charsets
 405      var $locale_to_charset = array(
 406          'japanese.euc' => 'euc-jp',
 407          'ja_jp.ujis' => 'euc-jp',
 408          'korean.euc' => 'euc-kr',
 409          'sr@Latn' => 'iso-8859-2',
 410          'zh_cn' => 'gb2312',
 411          'zh_hk' => 'big5',
 412          'zh_tw' => 'big5',
 413      );
 414  
 415          // TYPO3 specific: Array with the system charsets used for each system language in TYPO3:
 416          // Empty values means "iso-8859-1"
 417      var $charSetArray = array(
 418          'af' => '',
 419          'ar' => 'iso-8859-6',
 420          'ba' => 'iso-8859-2',
 421          'bg' => 'windows-1251',
 422          'br' => '',
 423          'ca' => 'iso-8859-15',
 424          'ch' => 'gb2312',
 425          'cs' => 'windows-1250',
 426          'cz' => 'windows-1250',
 427          'da' => '',
 428          'de' => '',
 429          'dk' => '',
 430          'el' => 'iso-8859-7',
 431          'eo' => 'utf-8',
 432          'es' => '',
 433          'et' => 'iso-8859-4',
 434          'eu' => '',
 435          'fa' => 'utf-8',
 436          'fi' => '',
 437          'fo' => 'utf-8',
 438          'fr' => '',
 439          'fr_CA' => '',
 440          'ga' => '',
 441          'ge' => 'utf-8',
 442          'gl' => '',
 443          'gr' => 'iso-8859-7',
 444          'he' => 'utf-8',
 445          'hi' => 'utf-8',
 446          'hk' => 'big5',
 447          'hr' => 'windows-1250',
 448          'hu' => 'iso-8859-2',
 449          'is' => 'utf-8',
 450          'it' => '',
 451          'ja' => 'shift_jis',
 452          'jp' => 'shift_jis',
 453          'ka' => 'utf-8',
 454          'kl' => 'utf-8',
 455          'km' => 'utf-8',
 456          'ko' => 'euc-kr',
 457          'kr' => 'euc-kr',
 458          'lt' => 'windows-1257',
 459          'lv' => 'utf-8',
 460          'ms' => '',
 461          'my' => '',
 462          'nl' => '',
 463          'no' => '',
 464          'pl' => 'iso-8859-2',
 465          'pt' => '',
 466          'pt_BR' => '',
 467          'qc' => '',
 468          'ro' => 'iso-8859-2',
 469          'ru' => 'windows-1251',
 470          'se' => '',
 471          'si' => 'windows-1250',
 472          'sk' => 'windows-1250',
 473          'sl' => 'windows-1250',
 474          'sq' => 'utf-8',
 475          'sr' => 'utf-8',
 476          'sv' => '',
 477          'th' => 'iso-8859-11',
 478          'tr' => 'iso-8859-9',
 479          'ua' => 'windows-1251',
 480          'uk' => 'windows-1251',
 481          'vi' => 'utf-8',
 482          'vn' => 'utf-8',
 483          'zh' => 'big5',
 484      );
 485  
 486          // TYPO3 specific: Array with the iso names used for each system language in TYPO3:
 487          // Missing keys means: same as TYPO3
 488          // @deprecated since TYPO3 4.6, will be removed in TYPO3 6.0 - use t3lib_l10n_Locales::getIsoMapping()
 489      var $isoArray = array(
 490          'ba' => 'bs',
 491          'br' => 'pt_BR',
 492          'ch' => 'zh_CN',
 493          'cz' => 'cs',
 494          'dk' => 'da',
 495          'si' => 'sl',
 496          'se' => 'sv',
 497          'gl' => 'kl',
 498          'gr' => 'el',
 499          'hk' => 'zh_HK',
 500          'kr' => 'ko',
 501          'ua' => 'uk',
 502          'jp' => 'ja',
 503          'qc' => 'fr_CA',
 504          'vn' => 'vi',
 505          'ge' => 'ka',
 506          'ga' => 'gl',
 507      );
 508  
 509      /**
 510       * Default constructor.
 511       */
 512  	public function __construct() {
 513          $this->locales = t3lib_div::makeInstance('t3lib_l10n_Locales');
 514      }
 515  
 516      /**
 517       * Normalize - changes input character set to lowercase letters.
 518       *
 519       * @param    string        Input charset
 520       * @return    string        Normalized charset
 521       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
 522       */
 523  	function parse_charset($charset) {
 524          $charset = trim(strtolower($charset));
 525          if (isset($this->synonyms[$charset])) {
 526              $charset = $this->synonyms[$charset];
 527          }
 528  
 529          return $charset;
 530      }
 531  
 532      /**
 533       * Get the charset of a locale.
 534       *
 535       * ln            language
 536       * ln_CN         language / country
 537       * ln_CN.cs      language / country / charset
 538       * ln_CN.cs@mod  language / country / charset / modifier
 539       *
 540       * @param    string        Locale string
 541       * @return    string        Charset resolved for locale string
 542       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
 543       */
 544  	function get_locale_charset($locale) {
 545          $locale = strtolower($locale);
 546  
 547              // exact locale specific charset?
 548          if (isset($this->locale_to_charset[$locale])) {
 549              return $this->locale_to_charset[$locale];
 550          }
 551  
 552              // get modifier
 553          list($locale, $modifier) = explode('@', $locale);
 554  
 555              // locale contains charset: use it
 556          list($locale, $charset) = explode('.', $locale);
 557          if ($charset) {
 558              return $this->parse_charset($charset);
 559          }
 560  
 561              // modifier is 'euro' (after charset check, because of xx.utf-8@euro)
 562          if ($modifier == 'euro') {
 563              return 'iso-8859-15';
 564          }
 565  
 566              // get language
 567          list($language, $country) = explode('_', $locale);
 568          if (isset($this->lang_to_script[$language])) {
 569              $script = $this->lang_to_script[$language];
 570          }
 571  
 572          if (TYPO3_OS == 'WIN') {
 573              $cs = $this->script_to_charset_windows[$script] ? $this->script_to_charset_windows[$script] : 'windows-1252';
 574          } else {
 575              $cs = $this->script_to_charset_unix[$script] ? $this->script_to_charset_unix[$script] : 'utf-8';
 576          }
 577  
 578          return $cs;
 579      }
 580  
 581  
 582      /********************************************
 583       *
 584       * Charset Conversion functions
 585       *
 586       ********************************************/
 587  
 588      /**
 589       * Convert from one charset to another charset.
 590       *
 591       * @param    string        Input string
 592       * @param    string        From charset (the current charset of the string)
 593       * @param    string        To charset (the output charset wanted)
 594       * @param    boolean        If set, then characters that are not available in the destination character set will be encoded as numeric entities
 595       * @return    string        Converted string
 596       * @see convArray()
 597       */
 598  	function conv($str, $fromCS, $toCS, $useEntityForNoChar = 0) {
 599          if ($fromCS == $toCS) {
 600              return $str;
 601          }
 602  
 603              // PHP-libs don't support fallback to SGML entities, but UTF-8 handles everything
 604          if ($toCS == 'utf-8' || !$useEntityForNoChar) {
 605              switch ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_convMethod']) {
 606                  case 'mbstring':
 607                      $conv_str = mb_convert_encoding($str, $toCS, $fromCS);
 608                      if (FALSE !== $conv_str) {
 609                          return $conv_str;
 610                      } // returns FALSE for unsupported charsets
 611                      break;
 612  
 613                  case 'iconv':
 614                      $conv_str = iconv($fromCS, $toCS . '//TRANSLIT', $str);
 615                      if (FALSE !== $conv_str) {
 616                          return $conv_str;
 617                      }
 618                      break;
 619  
 620                  case 'recode':
 621                      $conv_str = recode_string($fromCS . '..' . $toCS, $str);
 622                      if (FALSE !== $conv_str) {
 623                          return $conv_str;
 624                      }
 625                      break;
 626              }
 627              // fallback to TYPO3 conversion
 628          }
 629  
 630          if ($fromCS != 'utf-8') {
 631              $str = $this->utf8_encode($str, $fromCS);
 632          }
 633          if ($toCS != 'utf-8') {
 634              $str = $this->utf8_decode($str, $toCS, $useEntityForNoChar);
 635          }
 636          return $str;
 637      }
 638  
 639      /**
 640       * Convert all elements in ARRAY with type string from one charset to another charset.
 641       * NOTICE: Array is passed by reference!
 642       *
 643       * @param    string        Input array, possibly multidimensional
 644       * @param    string        From charset (the current charset of the string)
 645       * @param    string        To charset (the output charset wanted)
 646       * @param    boolean        If set, then characters that are not available in the destination character set will be encoded as numeric entities
 647       * @return    void
 648       * @see conv()
 649       */
 650  	function convArray(&$array, $fromCS, $toCS, $useEntityForNoChar = 0) {
 651          foreach ($array as $key => $value) {
 652              if (is_array($array[$key])) {
 653                  $this->convArray($array[$key], $fromCS, $toCS, $useEntityForNoChar);
 654              } elseif (is_string($array[$key])) {
 655                  $array[$key] = $this->conv($array[$key], $fromCS, $toCS, $useEntityForNoChar);
 656              }
 657          }
 658      }
 659  
 660      /**
 661       * Converts $str from $charset to UTF-8
 662       *
 663       * @param    string        String in local charset to convert to UTF-8
 664       * @param    string        Charset, lowercase. Must be found in csconvtbl/ folder.
 665       * @return    string        Output string, converted to UTF-8
 666       */
 667  	function utf8_encode($str, $charset) {
 668  
 669          if ($charset === 'utf-8') {
 670              return $str;
 671          }
 672  
 673              // Charset is case-insensitive.
 674          if ($this->initCharset($charset)) { // Parse conv. table if not already...
 675              $strLen = strlen($str);
 676              $outStr = '';
 677  
 678              for ($a = 0; $a < $strLen; $a++) { // Traverse each char in string.
 679                  $chr = substr($str, $a, 1);
 680                  $ord = ord($chr);
 681                  if (isset($this->twoByteSets[$charset])) { // If the charset has two bytes per char
 682                      $ord2 = ord($str{$a + 1});
 683                      $ord = $ord << 8 | $ord2; // assume big endian
 684  
 685                      if (isset($this->parsedCharsets[$charset]['local'][$ord])) { // If the local char-number was found in parsed conv. table then we use that, otherwise 127 (no char?)
 686                          $outStr .= $this->parsedCharsets[$charset]['local'][$ord];
 687                      } else {
 688                          $outStr .= chr($this->noCharByteVal);
 689                      } // No char exists
 690                      $a++;
 691                  } elseif ($ord > 127) { // If char has value over 127 it's a multibyte char in UTF-8
 692                      if (isset($this->eucBasedSets[$charset])) { // EUC uses two-bytes above 127; we get both and advance pointer and make $ord a 16bit int.
 693                          if ($charset != 'shift_jis' || ($ord < 0xA0 || $ord > 0xDF)) { // Shift-JIS: chars between 160 and 223 are single byte
 694                              $a++;
 695                              $ord2 = ord(substr($str, $a, 1));
 696                              $ord = $ord * 256 + $ord2;
 697                          }
 698                      }
 699  
 700                      if (isset($this->parsedCharsets[$charset]['local'][$ord])) { // If the local char-number was found in parsed conv. table then we use that, otherwise 127 (no char?)
 701                          $outStr .= $this->parsedCharsets[$charset]['local'][$ord];
 702                      } else {
 703                          $outStr .= chr($this->noCharByteVal);
 704                      } // No char exists
 705                  } else {
 706                      $outStr .= $chr;
 707                  } // ... otherwise it's just ASCII 0-127 and one byte. Transparent
 708              }
 709              return $outStr;
 710          }
 711      }
 712  
 713      /**
 714       * Converts $str from UTF-8 to $charset
 715       *
 716       * @param    string        String in UTF-8 to convert to local charset
 717       * @param    string        Charset, lowercase. Must be found in csconvtbl/ folder.
 718       * @param    boolean        If set, then characters that are not available in the destination character set will be encoded as numeric entities
 719       * @return    string        Output string, converted to local charset
 720       */
 721  	function utf8_decode($str, $charset, $useEntityForNoChar = 0) {
 722  
 723          if ($charset === 'utf-8') {
 724              return $str;
 725          }
 726  
 727              // Charset is case-insensitive.
 728          if ($this->initCharset($charset)) { // Parse conv. table if not already...
 729              $strLen = strlen($str);
 730              $outStr = '';
 731              $buf = '';
 732              for ($a = 0, $i = 0; $a < $strLen; $a++, $i++) { // Traverse each char in UTF-8 string.
 733                  $chr = substr($str, $a, 1);
 734                  $ord = ord($chr);
 735                  if ($ord > 127) { // This means multibyte! (first byte!)
 736                      if ($ord & 64) { // Since the first byte must have the 7th bit set we check that. Otherwise we might be in the middle of a byte sequence.
 737  
 738                          $buf = $chr; // Add first byte
 739                          for ($b = 0; $b < 8; $b++) { // for each byte in multibyte string...
 740                              $ord = $ord << 1; // Shift it left and ...
 741                              if ($ord & 128) { // ... and with 8th bit - if that is set, then there are still bytes in sequence.
 742                                  $a++; // Increase pointer...
 743                                  $buf .= substr($str, $a, 1); // ... and add the next char.
 744                              } else {
 745                                  break;
 746                              }
 747                          }
 748  
 749                          if (isset($this->parsedCharsets[$charset]['utf8'][$buf])) { // If the UTF-8 char-sequence is found then...
 750                              $mByte = $this->parsedCharsets[$charset]['utf8'][$buf]; // The local number
 751                              if ($mByte > 255) { // If the local number is greater than 255 we will need to split the byte (16bit word assumed) in two chars.
 752                                  $outStr .= chr(($mByte >> 8) & 255) . chr($mByte & 255);
 753                              } else {
 754                                  $outStr .= chr($mByte);
 755                              }
 756                          } elseif ($useEntityForNoChar) { // Create num entity:
 757                              $outStr .= '&#' . $this->utf8CharToUnumber($buf, 1) . ';';
 758                          } else {
 759                              $outStr .= chr($this->noCharByteVal);
 760                          } // No char exists
 761                      } else {
 762                          $outStr .= chr($this->noCharByteVal);
 763                      } // No char exists (MIDDLE of MB sequence!)
 764                  } else {
 765                      $outStr .= $chr;
 766                  } // ... otherwise it's just ASCII 0-127 and one byte. Transparent
 767              }
 768              return $outStr;
 769          }
 770      }
 771  
 772      /**
 773       * Converts all chars > 127 to numeric entities.
 774       *
 775       * @param    string        Input string
 776       * @return    string        Output string
 777       */
 778  	function utf8_to_entities($str) {
 779          $strLen = strlen($str);
 780          $outStr = '';
 781          $buf = '';
 782          for ($a = 0; $a < $strLen; $a++) { // Traverse each char in UTF-8 string.
 783              $chr = substr($str, $a, 1);
 784              $ord = ord($chr);
 785              if ($ord > 127) { // This means multibyte! (first byte!)
 786                  if ($ord & 64) { // Since the first byte must have the 7th bit set we check that. Otherwise we might be in the middle of a byte sequence.
 787                      $buf = $chr; // Add first byte
 788                      for ($b = 0; $b < 8; $b++) { // for each byte in multibyte string...
 789                          $ord = $ord << 1; // Shift it left and ...
 790                          if ($ord & 128) { // ... and with 8th bit - if that is set, then there are still bytes in sequence.
 791                              $a++; // Increase pointer...
 792                              $buf .= substr($str, $a, 1); // ... and add the next char.
 793                          } else {
 794                              break;
 795                          }
 796                      }
 797  
 798                      $outStr .= '&#' . $this->utf8CharToUnumber($buf, 1) . ';';
 799                  } else {
 800                      $outStr .= chr($this->noCharByteVal);
 801                  } // No char exists (MIDDLE of MB sequence!)
 802              } else {
 803                  $outStr .= $chr;
 804              } // ... otherwise it's just ASCII 0-127 and one byte. Transparent
 805          }
 806  
 807          return $outStr;
 808      }
 809  
 810      /**
 811       * Converts numeric entities (UNICODE, eg. decimal (&#1234;) or hexadecimal (&#x1b;)) to UTF-8 multibyte chars
 812       *
 813       * @param    string        Input string, UTF-8
 814       * @param    boolean        If set, then all string-HTML entities (like &amp; or &pound; will be converted as well)
 815       * @return    string        Output string
 816       */
 817  	function entities_to_utf8($str, $alsoStdHtmlEnt = FALSE) {
 818          // Workaround for #39287: 3rd parameter for get_html_translation_table() was only added in PHP 5.3.4 and later
 819          // see http://php.net/manual/en/function.get-html-translation-table.php
 820          $applyPhpCompatibilityFix = version_compare(phpversion(), '5.3.4', '<');
 821  
 822          if ($alsoStdHtmlEnt) {
 823              if ($applyPhpCompatibilityFix === TRUE) {
 824                  $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_COMPAT));
 825              } else {
 826                  $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES, ENT_COMPAT, 'UTF-8'));
 827              }
 828          }
 829  
 830          $token = md5(microtime());
 831          $parts = explode($token, preg_replace('/(&([#[:alnum:]]*);)/', $token . '$2}' . $token, $str));
 832          foreach ($parts as $k => $v) {
 833                  // only take every second element
 834              if ($k % 2 === 0) {
 835                  continue;
 836              }
 837  
 838              $position = 0;
 839              if (substr($v, $position, 1) == '#') { // Dec or hex entities:
 840                  $position++;
 841                  if (substr($v, $position, 1) == 'x') {
 842                      $v = hexdec(substr($v, ++$position));
 843                  } else {
 844                      $v = substr($v, $position);
 845                  }
 846                  $parts[$k] = $this->UnumberToChar($v);
 847              } elseif ($alsoStdHtmlEnt && isset($trans_tbl['&' . $v . ';'])) { // Other entities:
 848                  $v = $trans_tbl['&' . $v . ';'];
 849                  if ($applyPhpCompatibilityFix === TRUE) {
 850                      $v = $this->utf8_encode($v, 'iso-8859-1');
 851                  }
 852                  $parts[$k] = $v;
 853              } else { // No conversion:
 854                  $parts[$k] = '&' . $v . ';';
 855              }
 856          }
 857  
 858          return implode('', $parts);
 859      }
 860  
 861      /**
 862       * Converts all chars in the input UTF-8 string into integer numbers returned in an array
 863       *
 864       * @param    string        Input string, UTF-8
 865       * @param    boolean        If set, then all HTML entities (like &amp; or &pound; or &#123; or &#x3f5d;) will be detected as characters.
 866       * @param    boolean        If set, then instead of integer numbers the real UTF-8 char is returned.
 867       * @return    array        Output array with the char numbers
 868       */
 869  	function utf8_to_numberarray($str, $convEntities = 0, $retChar = 0) {
 870              // If entities must be registered as well...:
 871          if ($convEntities) {
 872              $str = $this->entities_to_utf8($str, 1);
 873          }
 874              // Do conversion:
 875          $strLen = strlen($str);
 876          $outArr = array();
 877          $buf = '';
 878          for ($a = 0; $a < $strLen; $a++) { // Traverse each char in UTF-8 string.
 879              $chr = substr($str, $a, 1);
 880              $ord = ord($chr);
 881              if ($ord > 127) { // This means multibyte! (first byte!)
 882                  if ($ord & 64) { // Since the first byte must have the 7th bit set we check that. Otherwise we might be in the middle of a byte sequence.
 883                      $buf = $chr; // Add first byte
 884                      for ($b = 0; $b < 8; $b++) { // for each byte in multibyte string...
 885                          $ord = $ord << 1; // Shift it left and ...
 886                          if ($ord & 128) { // ... and with 8th bit - if that is set, then there are still bytes in sequence.
 887                              $a++; // Increase pointer...
 888                              $buf .= substr($str, $a, 1); // ... and add the next char.
 889                          } else {
 890                              break;
 891                          }
 892                      }
 893  
 894                      $outArr[] = $retChar ? $buf : $this->utf8CharToUnumber($buf);
 895                  } else {
 896                      $outArr[] = $retChar ? chr($this->noCharByteVal) : $this->noCharByteVal;
 897                  } // No char exists (MIDDLE of MB sequence!)
 898              } else {
 899                  $outArr[] = $retChar ? chr($ord) : $ord;
 900              } // ... otherwise it's just ASCII 0-127 and one byte. Transparent
 901          }
 902  
 903          return $outArr;
 904      }
 905  
 906      /**
 907       * Converts a UNICODE number to a UTF-8 multibyte character
 908       * Algorithm based on script found at From: http://czyborra.com/utf/
 909       * Unit-tested by Kasper
 910       *
 911       * The binary representation of the character's integer value is thus simply spread across the bytes and the number of high bits set in the lead byte announces the number of bytes in the multibyte sequence:
 912       *
 913       *  bytes | bits | representation
 914       *      1 |    7 | 0vvvvvvv
 915       *      2 |   11 | 110vvvvv 10vvvvvv
 916       *      3 |   16 | 1110vvvv 10vvvvvv 10vvvvvv
 917       *      4 |   21 | 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv
 918       *      5 |   26 | 111110vv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv
 919       *      6 |   31 | 1111110v 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv 10vvvvvv
 920       *
 921       * @param    integer        UNICODE integer
 922       * @return    string        UTF-8 multibyte character string
 923       * @see utf8CharToUnumber()
 924       */
 925  	function UnumberToChar($cbyte) {
 926          $str = '';
 927  
 928          if ($cbyte < 0x80) {
 929              $str .= chr($cbyte);
 930          } else {
 931              if ($cbyte < 0x800) {
 932                  $str .= chr(0xC0 | ($cbyte >> 6));
 933                  $str .= chr(0x80 | ($cbyte & 0x3F));
 934              } else {
 935                  if ($cbyte < 0x10000) {
 936                      $str .= chr(0xE0 | ($cbyte >> 12));
 937                      $str .= chr(0x80 | (($cbyte >> 6) & 0x3F));
 938                      $str .= chr(0x80 | ($cbyte & 0x3F));
 939                  } else {
 940                      if ($cbyte < 0x200000) {
 941                          $str .= chr(0xF0 | ($cbyte >> 18));
 942                          $str .= chr(0x80 | (($cbyte >> 12) & 0x3F));
 943                          $str .= chr(0x80 | (($cbyte >> 6) & 0x3F));
 944                          $str .= chr(0x80 | ($cbyte & 0x3F));
 945                      } else {
 946                          if ($cbyte < 0x4000000) {
 947                              $str .= chr(0xF8 | ($cbyte >> 24));
 948                              $str .= chr(0x80 | (($cbyte >> 18) & 0x3F));
 949                              $str .= chr(0x80 | (($cbyte >> 12) & 0x3F));
 950                              $str .= chr(0x80 | (($cbyte >> 6) & 0x3F));
 951                              $str .= chr(0x80 | ($cbyte & 0x3F));
 952                          } else {
 953                              if ($cbyte < 0x80000000) {
 954                                  $str .= chr(0xFC | ($cbyte >> 30));
 955                                  $str .= chr(0x80 | (($cbyte >> 24) & 0x3F));
 956                                  $str .= chr(0x80 | (($cbyte >> 18) & 0x3F));
 957                                  $str .= chr(0x80 | (($cbyte >> 12) & 0x3F));
 958                                  $str .= chr(0x80 | (($cbyte >> 6) & 0x3F));
 959                                  $str .= chr(0x80 | ($cbyte & 0x3F));
 960                              } else { // Cannot express a 32-bit character in UTF-8
 961                                  $str .= chr($this->noCharByteVal);
 962                              }
 963                          }
 964                      }
 965                  }
 966              }
 967          }
 968          return $str;
 969      }
 970  
 971      /**
 972       * Converts a UTF-8 Multibyte character to a UNICODE number
 973       * Unit-tested by Kasper
 974       *
 975       * @param    string        UTF-8 multibyte character string
 976       * @param    boolean        If set, then a hex. number is returned.
 977       * @return    integer        UNICODE integer
 978       * @see UnumberToChar()
 979       */
 980  	function utf8CharToUnumber($str, $hex = 0) {
 981          $ord = ord(substr($str, 0, 1)); // First char
 982  
 983          if (($ord & 192) == 192) { // This verifyes that it IS a multi byte string
 984              $binBuf = '';
 985              for ($b = 0; $b < 8; $b++) { // for each byte in multibyte string...
 986                  $ord = $ord << 1; // Shift it left and ...
 987                  if ($ord & 128) { // ... and with 8th bit - if that is set, then there are still bytes in sequence.
 988                      $binBuf .= substr('00000000' . decbin(ord(substr($str, $b + 1, 1))), -6);
 989                  } else {
 990                      break;
 991                  }
 992              }
 993              $binBuf = substr('00000000' . decbin(ord(substr($str, 0, 1))), -(6 - $b)) . $binBuf;
 994  
 995              $int = bindec($binBuf);
 996          } else {
 997              $int = $ord;
 998          }
 999  
1000          return $hex ? 'x' . dechex($int) : $int;
1001      }
1002  
1003  
1004      /********************************************
1005       *
1006       * Init functions
1007       *
1008       ********************************************/
1009  
1010      /**
1011       * This will initialize a charset for use if it's defined in the PATH_t3lib.'csconvtbl/' folder
1012       * This function is automatically called by the conversion functions
1013       *
1014       * PLEASE SEE: http://www.unicode.org/Public/MAPPINGS/
1015       *
1016       * @param    string        The charset to be initialized. Use lowercase charset always (the charset must match exactly with a filename in csconvtbl/ folder ([charset].tbl)
1017       * @return    integer        Returns '1' if already loaded. Returns FALSE if charset conversion table was not found. Returns '2' if the charset conversion table was found and parsed.
1018       * @access private
1019       */
1020  	function initCharset($charset) {
1021              // Only process if the charset is not yet loaded:
1022          if (!is_array($this->parsedCharsets[$charset])) {
1023  
1024                  // Conversion table filename:
1025              $charsetConvTableFile = PATH_t3lib . 'csconvtbl/' . $charset . '.tbl';
1026  
1027                  // If the conversion table is found:
1028              if ($charset && t3lib_div::validPathStr($charsetConvTableFile) && @is_file($charsetConvTableFile)) {
1029                      // Cache file for charsets:
1030                      // Caching brought parsing time for gb2312 down from 2400 ms to 150 ms. For other charsets we are talking 11 ms down to zero.
1031                  $cacheFile = t3lib_div::getFileAbsFileName('typo3temp/cs/charset_' . $charset . '.tbl');
1032                  if ($cacheFile && @is_file($cacheFile)) {
1033                      $this->parsedCharsets[$charset] = unserialize(t3lib_div::getUrl($cacheFile));
1034                  } else {
1035                          // Parse conversion table into lines:
1036                      $lines = t3lib_div::trimExplode(LF, t3lib_div::getUrl($charsetConvTableFile), 1);
1037                          // Initialize the internal variable holding the conv. table:
1038                      $this->parsedCharsets[$charset] = array('local' => array(), 'utf8' => array());
1039                          // traverse the lines:
1040                      $detectedType = '';
1041                      foreach ($lines as $value) {
1042                          if (trim($value) && substr($value, 0, 1) != '#') { // Comment line or blanks are ignored.
1043  
1044                                  // Detect type if not done yet: (Done on first real line)
1045                                  // The "whitespaced" type is on the syntax     "0x0A    0x000A    #LINE FEED"     while     "ms-token" is like         "B9 = U+00B9 : SUPERSCRIPT ONE"
1046                              if (!$detectedType) {
1047                                  $detectedType = preg_match('/[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+/', $value) ? 'whitespaced' : 'ms-token';
1048                              }
1049  
1050                              if ($detectedType == 'ms-token') {
1051                                  list($hexbyte, $utf8) = preg_split('/[=:]/', $value, 3);
1052                              } elseif ($detectedType == 'whitespaced') {
1053                                  $regA = array();
1054                                  preg_match('/[[:space:]]*0x([[:alnum:]]*)[[:space:]]+0x([[:alnum:]]*)[[:space:]]+/', $value, $regA);
1055                                  $hexbyte = $regA[1];
1056                                  $utf8 = 'U+' . $regA[2];
1057                              }
1058                              $decval = hexdec(trim($hexbyte));
1059                              if ($decval > 127) {
1060                                  $utf8decval = hexdec(substr(trim($utf8), 2));
1061                                  $this->parsedCharsets[$charset]['local'][$decval] = $this->UnumberToChar($utf8decval);
1062                                  $this->parsedCharsets[$charset]['utf8'][$this->parsedCharsets[$charset]['local'][$decval]] = $decval;
1063                              }
1064                          }
1065                      }
1066                      if ($cacheFile) {
1067                          t3lib_div::writeFileToTypo3tempDir($cacheFile, serialize($this->parsedCharsets[$charset]));
1068                      }
1069                  }
1070                  return 2;
1071              } else {
1072                  return FALSE;
1073              }
1074          } else {
1075              return 1;
1076          }
1077      }
1078  
1079      /**
1080       * This function initializes all UTF-8 character data tables.
1081       *
1082       * PLEASE SEE: http://www.unicode.org/Public/UNIDATA/
1083       *
1084       * @param    string        Mode ("case", "ascii", ...)
1085       * @return    integer        Returns FALSE on error, a TRUE value on success: 1 table already loaded, 2, cached version, 3 table parsed (and cached).
1086       * @access private
1087       */
1088  	function initUnicodeData($mode = NULL) {
1089              // cache files
1090          $cacheFileCase = t3lib_div::getFileAbsFileName('typo3temp/cs/cscase_utf-8.tbl');
1091          $cacheFileASCII = t3lib_div::getFileAbsFileName('typo3temp/cs/csascii_utf-8.tbl');
1092  
1093              // Only process if the tables are not yet loaded
1094          switch ($mode) {
1095              case 'case':
1096                  if (is_array($this->caseFolding['utf-8'])) {
1097                      return 1;
1098                  }
1099  
1100                      // Use cached version if possible
1101                  if ($cacheFileCase && @is_file($cacheFileCase)) {
1102                      $this->caseFolding['utf-8'] = unserialize(t3lib_div::getUrl($cacheFileCase));
1103                      return 2;
1104                  }
1105                  break;
1106  
1107              case 'ascii':
1108                  if (is_array($this->toASCII['utf-8'])) {
1109                      return 1;
1110                  }
1111  
1112                      // Use cached version if possible
1113                  if ($cacheFileASCII && @is_file($cacheFileASCII)) {
1114                      $this->toASCII['utf-8'] = unserialize(t3lib_div::getUrl($cacheFileASCII));
1115                      return 2;
1116                  }
1117                  break;
1118          }
1119  
1120              // process main Unicode data file
1121          $unicodeDataFile = PATH_t3lib . 'unidata/UnicodeData.txt';
1122          if (!(t3lib_div::validPathStr($unicodeDataFile) && @is_file($unicodeDataFile))) {
1123              return FALSE;
1124          }
1125  
1126          $fh = fopen($unicodeDataFile, 'rb');
1127          if (!$fh) {
1128              return FALSE;
1129          }
1130  
1131              // key = utf8 char (single codepoint), value = utf8 string (codepoint sequence)
1132              // note: we use the UTF-8 characters here and not the Unicode numbers to avoid conversion roundtrip in utf8_strtolower/-upper)
1133          $this->caseFolding['utf-8'] = array();
1134          $utf8CaseFolding =& $this->caseFolding['utf-8']; // a shorthand
1135          $utf8CaseFolding['toUpper'] = array();
1136          $utf8CaseFolding['toLower'] = array();
1137          $utf8CaseFolding['toTitle'] = array();
1138  
1139          $decomposition = array(); // array of temp. decompositions
1140          $mark = array(); // array of chars that are marks (eg. composing accents)
1141          $number = array(); // array of chars that are numbers (eg. digits)
1142          $omit = array(); // array of chars to be omitted (eg. Russian hard sign)
1143  
1144          while (!feof($fh)) {
1145              $line = fgets($fh, 4096);
1146                  // has a lot of info
1147              list($char, $name, $cat, , , $decomp, , , $num, , , , $upper, $lower, $title,) = explode(';', rtrim($line));
1148  
1149              $ord = hexdec($char);
1150              if ($ord > 0xFFFF) {
1151                  break;
1152              } // only process the BMP
1153  
1154              $utf8_char = $this->UnumberToChar($ord);
1155  
1156              if ($upper) {
1157                  $utf8CaseFolding['toUpper'][$utf8_char] = $this->UnumberToChar(hexdec($upper));
1158              }
1159              if ($lower) {
1160                  $utf8CaseFolding['toLower'][$utf8_char] = $this->UnumberToChar(hexdec($lower));
1161              }
1162                  // store "title" only when different from "upper" (only a few)
1163              if ($title && $title != $upper) {
1164                  $utf8CaseFolding['toTitle'][$utf8_char] = $this->UnumberToChar(hexdec($title));
1165              }
1166  
1167              switch ($cat{0}) {
1168                  case 'M': // mark (accent, umlaut, ...)
1169                      $mark["U+$char"] = 1;
1170                      break;
1171  
1172                  case 'N': // numeric value
1173                      if ($ord > 0x80 && $num != '') {
1174                          $number["U+$char"] = $num;
1175                      }
1176              }
1177  
1178                  // accented Latin letters without "official" decomposition
1179              $match = array();
1180              if (preg_match('/^LATIN (SMALL|CAPITAL) LETTER ([A-Z]) WITH/', $name, $match) && !$decomp) {
1181                  $c = ord($match[2]);
1182                  if ($match[1] == 'SMALL') {
1183                      $c += 32;
1184                  }
1185  
1186                  $decomposition["U+$char"] = array(dechex($c));
1187                  continue;
1188              }
1189  
1190              $match = array();
1191              if (preg_match('/(<.*>)? *(.+)/', $decomp, $match)) {
1192                  switch ($match[1]) {
1193                      case '<circle>': // add parenthesis as circle replacement, eg (1)
1194                          $match[2] = '0028 ' . $match[2] . ' 0029';
1195                          break;
1196  
1197                      case '<square>': // add square brackets as square replacement, eg [1]
1198                          $match[2] = '005B ' . $match[2] . ' 005D';
1199                          break;
1200  
1201                      case '<compat>': // ignore multi char decompositions that start with a space
1202                          if (preg_match('/^0020 /', $match[2])) {
1203                              continue 2;
1204                          }
1205                          break;
1206  
1207                          // ignore Arabic and vertical layout presentation decomposition
1208                      case '<initial>':
1209                      case '<medial>':
1210                      case '<final>':
1211                      case '<isolated>':
1212                      case '<vertical>':
1213                          continue 2;
1214                  }
1215                  $decomposition["U+$char"] = explode(' ', $match[2]);
1216              }
1217          }
1218          fclose($fh);
1219  
1220              // process additional Unicode data for casing (allow folded characters to expand into a sequence)
1221          $specialCasingFile = PATH_t3lib . 'unidata/SpecialCasing.txt';
1222          if (t3lib_div::validPathStr($specialCasingFile) && @is_file($specialCasingFile)) {
1223              $fh = fopen($specialCasingFile, 'rb');
1224              if ($fh) {
1225                  while (!feof($fh)) {
1226                      $line = fgets($fh, 4096);
1227                      if ($line{0} != '#' && trim($line) != '') {
1228  
1229                          list($char, $lower, $title, $upper, $cond) = t3lib_div::trimExplode(';', $line);
1230                          if ($cond == '' || $cond{0} == '#') {
1231                              $utf8_char = $this->UnumberToChar(hexdec($char));
1232                              if ($char != $lower) {
1233                                  $arr = explode(' ', $lower);
1234                                  for ($i = 0; isset($arr[$i]); $i++) {
1235                                      $arr[$i] = $this->UnumberToChar(hexdec($arr[$i]));
1236                                  }
1237                                  $utf8CaseFolding['toLower'][$utf8_char] = implode('', $arr);
1238                              }
1239                              if ($char != $title && $title != $upper) {
1240                                  $arr = explode(' ', $title);
1241                                  for ($i = 0; isset($arr[$i]); $i++) {
1242                                      $arr[$i] = $this->UnumberToChar(hexdec($arr[$i]));
1243                                  }
1244                                  $utf8CaseFolding['toTitle'][$utf8_char] = implode('', $arr);
1245                              }
1246                              if ($char != $upper) {
1247                                  $arr = explode(' ', $upper);
1248                                  for ($i = 0; isset($arr[$i]); $i++) {
1249                                      $arr[$i] = $this->UnumberToChar(hexdec($arr[$i]));
1250                                  }
1251                                  $utf8CaseFolding['toUpper'][$utf8_char] = implode('', $arr);
1252                              }
1253                          }
1254                      }
1255                  }
1256                  fclose($fh);
1257              }
1258          }
1259  
1260              // process custom decompositions
1261          $customTranslitFile = PATH_t3lib . 'unidata/Translit.txt';
1262          if (t3lib_div::validPathStr($customTranslitFile) && @is_file($customTranslitFile)) {
1263              $fh = fopen($customTranslitFile, 'rb');
1264              if ($fh) {
1265                  while (!feof($fh)) {
1266                      $line = fgets($fh, 4096);
1267                      if ($line{0} != '#' && trim($line) != '') {
1268                          list($char, $translit) = t3lib_div::trimExplode(';', $line);
1269                          if (!$translit) {
1270                              $omit["U+$char"] = 1;
1271                          }
1272                          $decomposition["U+$char"] = explode(' ', $translit);
1273  
1274                      }
1275                  }
1276                  fclose($fh);
1277              }
1278          }
1279  
1280              // decompose and remove marks; inspired by unac (Loic Dachary <loic@senga.org>)
1281          foreach ($decomposition as $from => $to) {
1282              $code_decomp = array();
1283  
1284              while ($code_value = array_shift($to)) {
1285                  if (isset($decomposition["U+$code_value"])) { // do recursive decomposition
1286                      foreach (array_reverse($decomposition["U+$code_value"]) as $cv) {
1287                          array_unshift($to, $cv);
1288                      }
1289                  } elseif (!isset($mark["U+$code_value"])) { // remove mark
1290                      array_push($code_decomp, $code_value);
1291                  }
1292              }
1293              if (count($code_decomp) || isset($omit[$from])) {
1294                  $decomposition[$from] = $code_decomp;
1295              } else {
1296                  unset($decomposition[$from]);
1297              }
1298          }
1299  
1300              // create ascii only mapping
1301          $this->toASCII['utf-8'] = array();
1302          $ascii =& $this->toASCII['utf-8'];
1303  
1304          foreach ($decomposition as $from => $to) {
1305              $code_decomp = array();
1306              while ($code_value = array_shift($to)) {
1307                  $ord = hexdec($code_value);
1308                  if ($ord > 127) {
1309                      continue 2;
1310                  } // skip decompositions containing non-ASCII chars
1311                  else
1312                  {
1313                      array_push($code_decomp, chr($ord));
1314                  }
1315              }
1316              $ascii[$this->UnumberToChar(hexdec($from))] = join('', $code_decomp);
1317          }
1318  
1319              // add numeric decompositions
1320          foreach ($number as $from => $to) {
1321              $utf8_char = $this->UnumberToChar(hexdec($from));
1322              if (!isset($ascii[$utf8_char])) {
1323                  $ascii[$utf8_char] = $to;
1324              }
1325          }
1326  
1327          if ($cacheFileCase) {
1328              t3lib_div::writeFileToTypo3tempDir($cacheFileCase, serialize($utf8CaseFolding));
1329          }
1330  
1331          if ($cacheFileASCII) {
1332              t3lib_div::writeFileToTypo3tempDir($cacheFileASCII, serialize($ascii));
1333          }
1334  
1335          return 3;
1336      }
1337  
1338      /**
1339       * This function initializes the folding table for a charset other than UTF-8.
1340       * This function is automatically called by the case folding functions.
1341       *
1342       * @param    string        Charset for which to initialize case folding.
1343       * @return    integer        Returns FALSE on error, a TRUE value on success: 1 table already loaded, 2, cached version, 3 table parsed (and cached).
1344       * @access private
1345       */
1346  	function initCaseFolding($charset) {
1347              // Only process if the case table is not yet loaded:
1348          if (is_array($this->caseFolding[$charset])) {
1349              return 1;
1350          }
1351  
1352              // Use cached version if possible
1353          $cacheFile = t3lib_div::getFileAbsFileName('typo3temp/cs/cscase_' . $charset . '.tbl');
1354          if ($cacheFile && @is_file($cacheFile)) {
1355              $this->caseFolding[$charset] = unserialize(t3lib_div::getUrl($cacheFile));
1356              return 2;
1357          }
1358  
1359              // init UTF-8 conversion for this charset
1360          if (!$this->initCharset($charset)) {
1361              return FALSE;
1362          }
1363  
1364              // UTF-8 case folding is used as the base conversion table
1365          if (!$this->initUnicodeData('case')) {
1366              return FALSE;
1367          }
1368  
1369          $nochar = chr($this->noCharByteVal);
1370          foreach ($this->parsedCharsets[$charset]['local'] as $ci => $utf8) {
1371                  // reconvert to charset (don't use chr() of numeric value, might be muli-byte)
1372              $c = $this->utf8_decode($utf8, $charset);
1373  
1374                  // $cc = $this->conv($this->caseFolding['utf-8']['toUpper'][$utf8], 'utf-8', $charset);
1375              $cc = $this->utf8_decode($this->caseFolding['utf-8']['toUpper'][$utf8], $charset);
1376              if ($cc != '' && $cc != $nochar) {
1377                  $this->caseFolding[$charset]['toUpper'][$c] = $cc;
1378              }
1379  
1380                  // $cc = $this->conv($this->caseFolding['utf-8']['toLower'][$utf8], 'utf-8', $charset);
1381              $cc = $this->utf8_decode($this->caseFolding['utf-8']['toLower'][$utf8], $charset);
1382              if ($cc != '' && $cc != $nochar) {
1383                  $this->caseFolding[$charset]['toLower'][$c] = $cc;
1384              }
1385  
1386                  // $cc = $this->conv($this->caseFolding['utf-8']['toTitle'][$utf8], 'utf-8', $charset);
1387              $cc = $this->utf8_decode($this->caseFolding['utf-8']['toTitle'][$utf8], $charset);
1388              if ($cc != '' && $cc != $nochar) {
1389                  $this->caseFolding[$charset]['toTitle'][$c] = $cc;
1390              }
1391          }
1392  
1393              // add the ASCII case table
1394          for ($i = ord('a'); $i <= ord('z'); $i++) {
1395              $this->caseFolding[$charset]['toUpper'][chr($i)] = chr($i - 32);
1396          }
1397          for ($i = ord('A'); $i <= ord('Z'); $i++) {
1398              $this->caseFolding[$charset]['toLower'][chr($i)] = chr($i + 32);
1399          }
1400  
1401          if ($cacheFile) {
1402              t3lib_div::writeFileToTypo3tempDir($cacheFile, serialize($this->caseFolding[$charset]));
1403          }
1404  
1405          return 3;
1406      }
1407  
1408      /**
1409       * This function initializes the to-ASCII conversion table for a charset other than UTF-8.
1410       * This function is automatically called by the ASCII transliteration functions.
1411       *
1412       * @param    string        Charset for which to initialize conversion.
1413       * @return    integer        Returns FALSE on error, a TRUE value on success: 1 table already loaded, 2, cached version, 3 table parsed (and cached).
1414       * @access private
1415       */
1416  	function initToASCII($charset) {
1417              // Only process if the case table is not yet loaded:
1418          if (is_array($this->toASCII[$charset])) {
1419              return 1;
1420          }
1421  
1422              // Use cached version if possible
1423          $cacheFile = t3lib_div::getFileAbsFileName('typo3temp/cs/csascii_' . $charset . '.tbl');
1424          if ($cacheFile && @is_file($cacheFile)) {
1425              $this->toASCII[$charset] = unserialize(t3lib_div::getUrl($cacheFile));
1426              return 2;
1427          }
1428  
1429              // init UTF-8 conversion for this charset
1430          if (!$this->initCharset($charset)) {
1431              return FALSE;
1432          }
1433  
1434              // UTF-8/ASCII transliteration is used as the base conversion table
1435          if (!$this->initUnicodeData('ascii')) {
1436              return FALSE;
1437          }
1438  
1439          $nochar = chr($this->noCharByteVal);
1440          foreach ($this->parsedCharsets[$charset]['local'] as $ci => $utf8) {
1441                  // reconvert to charset (don't use chr() of numeric value, might be muli-byte)
1442              $c = $this->utf8_decode($utf8, $charset);
1443  
1444              if (isset($this->toASCII['utf-8'][$utf8])) {
1445                  $this->toASCII[$charset][$c] = $this->toASCII['utf-8'][$utf8];
1446              }
1447          }
1448  
1449          if ($cacheFile) {
1450              t3lib_div::writeFileToTypo3tempDir($cacheFile, serialize($this->toASCII[$charset]));
1451          }
1452  
1453          return 3;
1454      }
1455  
1456  
1457      /********************************************
1458       *
1459       * String operation functions
1460       *
1461       ********************************************/
1462  
1463      /**
1464       * Returns a part of a string.
1465       * Unit-tested by Kasper (single byte charsets only)
1466       *
1467       * @param    string        The character set
1468       * @param    string        Character string
1469       * @param    integer        Start position (character position)
1470       * @param    integer        Length (in characters)
1471       * @return    string        The substring
1472       * @see substr(), mb_substr()
1473       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1474       */
1475  	function substr($charset, $string, $start, $len = NULL) {
1476          if ($len === 0 || $string === '') {
1477              return '';
1478          }
1479  
1480          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1481                  // cannot omit $len, when specifying charset
1482              if ($len == NULL) {
1483                  $enc = mb_internal_encoding(); // save internal encoding
1484                  mb_internal_encoding($charset);
1485                  $str = mb_substr($string, $start);
1486                  mb_internal_encoding($enc); // restore internal encoding
1487  
1488                  return $str;
1489              }
1490              else {
1491                  return mb_substr($string, $start, $len, $charset);
1492              }
1493          } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'iconv') {
1494                  // cannot omit $len, when specifying charset
1495              if ($len == NULL) {
1496                  $enc = iconv_get_encoding('internal_encoding'); // save internal encoding
1497                  iconv_set_encoding('internal_encoding', $charset);
1498                  $str = iconv_substr($string, $start);
1499                  iconv_set_encoding('internal_encoding', $enc); // restore internal encoding
1500  
1501                  return $str;
1502              }
1503              else {
1504                  return iconv_substr($string, $start, $len, $charset);
1505              }
1506          } elseif ($charset == 'utf-8') {
1507              return $this->utf8_substr($string, $start, $len);
1508          } elseif ($this->eucBasedSets[$charset]) {
1509              return $this->euc_substr($string, $start, $charset, $len);
1510          } elseif ($this->twoByteSets[$charset]) {
1511              return substr($string, $start * 2, $len * 2);
1512          } elseif ($this->fourByteSets[$charset]) {
1513              return substr($string, $start * 4, $len * 4);
1514          }
1515  
1516              // treat everything else as single-byte encoding
1517          return $len === NULL ? substr($string, $start) : substr($string, $start, $len);
1518      }
1519  
1520      /**
1521       * Counts the number of characters.
1522       * Unit-tested by Kasper (single byte charsets only)
1523       *
1524       * @param    string        The character set
1525       * @param    string        Character string
1526       * @return    integer        The number of characters
1527       * @see strlen()
1528       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1529       */
1530  	function strlen($charset, $string) {
1531          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1532              return mb_strlen($string, $charset);
1533          } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'iconv') {
1534              return iconv_strlen($string, $charset);
1535          } elseif ($charset == 'utf-8') {
1536              return $this->utf8_strlen($string);
1537          } elseif ($this->eucBasedSets[$charset]) {
1538              return $this->euc_strlen($string, $charset);
1539          } elseif ($this->twoByteSets[$charset]) {
1540              return strlen($string) / 2;
1541          } elseif ($this->fourByteSets[$charset]) {
1542              return strlen($string) / 4;
1543          }
1544              // treat everything else as single-byte encoding
1545          return strlen($string);
1546      }
1547  
1548      /**
1549       * Method to crop strings using the mb_substr function.
1550       *
1551       * @param  string        The character set
1552       * @param  string        String to be cropped
1553       * @param  integer        Crop length (in characters)
1554       * @param  string        Crop signifier
1555       * @return string        The shortened string
1556       * @see mb_strlen(), mb_substr()
1557       */
1558  	protected function cropMbstring($charset, $string, $len, $crop = '') {
1559          if (intval($len) === 0 || mb_strlen($string, $charset) <= abs($len)) {
1560              return $string;
1561          }
1562  
1563          if ($len > 0) {
1564              $string = mb_substr($string, 0, $len, $charset) . $crop;
1565          } else {
1566              $string = $crop . mb_substr($string, $len, mb_strlen($string, $charset), $charset);
1567          }
1568  
1569          return $string;
1570      }
1571  
1572      /**
1573       * Truncates a string and pre-/appends a string.
1574       * Unit tested by Kasper
1575       *
1576       * @param    string        The character set
1577       * @param    string        Character string
1578       * @param    integer        Length (in characters)
1579       * @param    string        Crop signifier
1580       * @return    string        The shortened string
1581       * @see substr(), mb_strimwidth()
1582       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1583       */
1584  	function crop($charset, $string, $len, $crop = '') {
1585          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1586              return $this->cropMbstring($charset, $string, $len, $crop);
1587          }
1588  
1589          if (intval($len) == 0) {
1590              return $string;
1591          }
1592  
1593          if ($charset == 'utf-8') {
1594              $i = $this->utf8_char2byte_pos($string, $len);
1595          } elseif ($this->eucBasedSets[$charset]) {
1596              $i = $this->euc_char2byte_pos($string, $len, $charset);
1597          } else {
1598              if ($len > 0) {
1599                  $i = $len;
1600              } else {
1601                  $i = strlen($string) + $len;
1602                  if ($i <= 0) {
1603                      $i = FALSE;
1604                  }
1605              }
1606          }
1607  
1608          if ($i === FALSE) { // $len outside actual string length
1609              return $string;
1610          } else {
1611              if ($len > 0) {
1612                  if (strlen($string{$i})) {
1613                      return substr($string, 0, $i) . $crop;
1614  
1615                  }
1616              } else {
1617                  if (strlen($string{$i - 1})) {
1618                      return $crop . substr($string, $i);
1619                  }
1620              }
1621  
1622              /*
1623                 if (abs($len)<$this->strlen($charset,$string))    {    // Has to use ->strlen() - otherwise multibyte strings ending with a multibyte char will return TRUE here (which is not a catastrophe, but...)
1624                     if ($len > 0)    {
1625                         return substr($string,0,$i).$crop;
1626                     } else {
1627                         return $crop.substr($string,$i);
1628                     }
1629                 }
1630     */
1631          }
1632          return $string;
1633      }
1634  
1635      /**
1636       * Cuts a string short at a given byte length.
1637       *
1638       * @param    string        The character set
1639       * @param    string        Character string
1640       * @param    integer        The byte length
1641       * @return    string        The shortened string
1642       * @see mb_strcut()
1643       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1644       */
1645  	function strtrunc($charset, $string, $len) {
1646          if ($len <= 0) {
1647              return '';
1648          }
1649  
1650          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1651              return mb_strcut($string, 0, $len, $charset);
1652          } elseif ($charset == 'utf-8') {
1653              return $this->utf8_strtrunc($string, $len);
1654          } elseif ($this->eucBasedSets[$charset]) {
1655              return $this->euc_strtrunc($string, $len, $charset);
1656          } elseif ($this->twoByteSets[$charset]) {
1657              if ($len % 2) {
1658                  $len--;
1659              } // don't cut at odd positions
1660          } elseif ($this->fourByteSets[$charset]) {
1661              $x = $len % 4;
1662              $len -= $x; // realign to position dividable by four
1663          }
1664              // treat everything else as single-byte encoding
1665          return substr($string, 0, $len);
1666      }
1667  
1668      /**
1669       * Translates all characters of a string into their respective case values.
1670       * Unlike strtolower() and strtoupper() this method is locale independent.
1671       * Note that the string length may change!
1672       * eg. lower case German "ß" (sharp S) becomes upper case "SS"
1673       * Unit-tested by Kasper
1674       * Real case folding is language dependent, this method ignores this fact.
1675       *
1676       * @param    string        Character set of string
1677       * @param    string        Input string to convert case for
1678       * @param    string        Case keyword: "toLower" means lowercase conversion, anything else is uppercase (use "toUpper" )
1679       * @return    string        The converted string
1680       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1681       * @see strtolower(), strtoupper()
1682       */
1683  	function conv_case($charset, $string, $case) {
1684          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1685              if ($case == 'toLower') {
1686                  $string = mb_strtolower($string, $charset);
1687              } else {
1688                  $string = mb_strtoupper($string, $charset);
1689              }
1690          } elseif ($charset == 'utf-8') {
1691              $string = $this->utf8_char_mapping($string, 'case', $case);
1692          } elseif (isset($this->eucBasedSets[$charset])) {
1693              $string = $this->euc_char_mapping($string, $charset, 'case', $case);
1694          } else {
1695                  // treat everything else as single-byte encoding
1696              $string = $this->sb_char_mapping($string, $charset, 'case', $case);
1697          }
1698  
1699          return $string;
1700      }
1701  
1702      /**
1703       * Equivalent of lcfirst/ucfirst but using character set.
1704       *
1705       * @param string $charset
1706       * @param string $string
1707       * @param string $case
1708       * @return string
1709       * @see t3lib_cs::conv_case()
1710       */
1711  	public function convCaseFirst($charset, $string, $case) {
1712          $firstChar = $this->substr($charset, $string, 0, 1);
1713          $firstChar = $this->conv_case($charset, $firstChar, $case);
1714          $remainder = $this->substr($charset, $string, 1);
1715          return $firstChar . $remainder;
1716      }
1717  
1718      /**
1719       * Converts special chars (like æøåÆØÅ, umlauts etc) to ascii equivalents (usually double-bytes, like æ => ae etc.)
1720       *
1721       * @param string $charset Character set of string
1722       * @param string $string Input string to convert
1723       * @return string The converted string
1724       */
1725  	function specCharsToASCII($charset, $string) {
1726          if ($charset == 'utf-8') {
1727              $string = $this->utf8_char_mapping($string, 'ascii');
1728          } elseif (isset($this->eucBasedSets[$charset])) {
1729              $string = $this->euc_char_mapping($string, $charset, 'ascii');
1730          } else {
1731                  // treat everything else as single-byte encoding
1732              $string = $this->sb_char_mapping($string, $charset, 'ascii');
1733          }
1734  
1735          return $string;
1736      }
1737  
1738  
1739      /**
1740       * converts the language codes that we get from the client (usually HTTP_ACCEPT_LANGUAGE)
1741       * into a TYPO3-readable language code
1742       * @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'
1743       *             see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
1744       * @return    string    a preferred language that TYPO3 supports, or "default" if none found
1745       * @author    Benjamin Mack (benni.typo3.org)
1746       */
1747  	public function getPreferredClientLanguage($languageCodesList) {
1748          $allLanguageCodes = array();
1749          $selectedLanguage = 'default';
1750  
1751              // get all languages where TYPO3 code is the same as the ISO code
1752          foreach ($this->charSetArray as $typo3Lang => $charSet) {
1753              $allLanguageCodes[$typo3Lang] = $typo3Lang;
1754          }
1755  
1756              // get all languages where TYPO3 code differs from ISO code
1757              // or needs the country part
1758              // the iso codes will here overwrite the default typo3 language in the key
1759          foreach ($this->locales->getIsoMapping() as $typo3Lang => $isoLang) {
1760              $isoLang = join('-', explode('_', $isoLang));
1761              $allLanguageCodes[$typo3Lang] = $isoLang;
1762          }
1763  
1764              // move the iso codes to the (because we're comparing the keys with "isset" later on)
1765          $allLanguageCodes = array_flip($allLanguageCodes);
1766  
1767  
1768          $preferredLanguages = t3lib_div::trimExplode(',', $languageCodesList);
1769              // order the preferred languages after they key
1770          $sortedPreferredLanguages = array();
1771          foreach ($preferredLanguages as $preferredLanguage) {
1772              $quality = 1.0;
1773              if (strpos($preferredLanguage, ';q=') !== FALSE) {
1774                  list($preferredLanguage, $quality) = explode(';q=', $preferredLanguage);
1775              }
1776              $sortedPreferredLanguages[$preferredLanguage] = $quality;
1777          }
1778  
1779              // loop through the languages, with the highest priority first
1780          arsort($sortedPreferredLanguages, SORT_NUMERIC);
1781          foreach ($sortedPreferredLanguages as $preferredLanguage => $quality) {
1782              if (isset($allLanguageCodes[$preferredLanguage])) {
1783                  $selectedLanguage = $allLanguageCodes[$preferredLanguage];
1784                  break;
1785              }
1786  
1787                  // strip the country code from the end
1788              list($preferredLanguage, $preferredCountry) = explode('-', $preferredLanguage);
1789              if (isset($allLanguageCodes[$preferredLanguage])) {
1790                  $selectedLanguage = $allLanguageCodes[$preferredLanguage];
1791                  break;
1792              }
1793          }
1794          if (!$selectedLanguage || $selectedLanguage == 'en') {
1795              $selectedLanguage = 'default';
1796          }
1797          return $selectedLanguage;
1798      }
1799  
1800  
1801      /********************************************
1802       *
1803       * Internal string operation functions
1804       *
1805       ********************************************/
1806  
1807      /**
1808       * Maps all characters of a string in a single byte charset.
1809       *
1810       * @param    string        the string
1811       * @param    string        the charset
1812       * @param    string        mode: 'case' (case folding) or 'ascii' (ASCII transliteration)
1813       * @param    string        'case': conversion 'toLower' or 'toUpper'
1814       * @return    string        the converted string
1815       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1816       */
1817  	function sb_char_mapping($str, $charset, $mode, $opt = '') {
1818          switch ($mode) {
1819              case 'case':
1820                  if (!$this->initCaseFolding($charset)) {
1821                      return $str;
1822                  } // do nothing
1823                  $map =& $this->caseFolding[$charset][$opt];
1824                  break;
1825  
1826              case 'ascii':
1827                  if (!$this->initToASCII($charset)) {
1828                      return $str;
1829                  } // do nothing
1830                  $map =& $this->toASCII[$charset];
1831                  break;
1832  
1833              default:
1834                  return $str;
1835          }
1836  
1837          $out = '';
1838          for ($i = 0; strlen($str{$i}); $i++) {
1839              $c = $str{$i};
1840              if (isset($map[$c])) {
1841                  $out .= $map[$c];
1842              } else {
1843                  $out .= $c;
1844              }
1845          }
1846  
1847          return $out;
1848      }
1849  
1850  
1851      /********************************************
1852       *
1853       * Internal UTF-8 string operation functions
1854       *
1855       ********************************************/
1856  
1857      /**
1858       * Returns a part of a UTF-8 string.
1859       * Unit-tested by Kasper and works 100% like substr() / mb_substr() for full range of $start/$len
1860       *
1861       * @param    string        UTF-8 string
1862       * @param    integer        Start position (character position)
1863       * @param    integer        Length (in characters)
1864       * @return    string        The substring
1865       * @see substr()
1866       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1867       */
1868  	function utf8_substr($str, $start, $len = NULL) {
1869          if (!strcmp($len, '0')) {
1870              return '';
1871          }
1872  
1873          $byte_start = $this->utf8_char2byte_pos($str, $start);
1874          if ($byte_start === FALSE) {
1875              if ($start > 0) {
1876                  return FALSE; // $start outside string length
1877              } else {
1878                  $start = 0;
1879              }
1880          }
1881  
1882          $str = substr($str, $byte_start);
1883  
1884          if ($len != NULL) {
1885              $byte_end = $this->utf8_char2byte_pos($str, $len);
1886              if ($byte_end === FALSE) // $len outside actual string length
1887              {
1888                  return $len < 0 ? '' : $str;
1889              } // When length is less than zero and exceeds, then we return blank string.
1890              else
1891              {
1892                  return substr($str, 0, $byte_end);
1893              }
1894          }
1895          else    {
1896              return $str;
1897          }
1898      }
1899  
1900      /**
1901       * Counts the number of characters of a string in UTF-8.
1902       * Unit-tested by Kasper and works 100% like strlen() / mb_strlen()
1903       *
1904       * @param    string        UTF-8 multibyte character string
1905       * @return    integer        The number of characters
1906       * @see strlen()
1907       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1908       */
1909  	function utf8_strlen($str) {
1910          $n = 0;
1911          for ($i = 0; strlen($str{$i}); $i++) {
1912              $c = ord($str{$i});
1913              if (!($c & 0x80)) // single-byte (0xxxxxx)
1914              {
1915                  $n++;
1916              }
1917              elseif (($c & 0xC0) == 0xC0) // multi-byte starting byte (11xxxxxx)
1918              {
1919                  $n++;
1920              }
1921          }
1922          return $n;
1923      }
1924  
1925      /**
1926       * Truncates a string in UTF-8 short at a given byte length.
1927       *
1928       * @param    string        UTF-8 multibyte character string
1929       * @param    integer        the byte length
1930       * @return    string        the shortened string
1931       * @see mb_strcut()
1932       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1933       */
1934  	function utf8_strtrunc($str, $len) {
1935          $i = $len - 1;
1936          if (ord($str{$i}) & 0x80) { // part of a multibyte sequence
1937              for (; $i > 0 && !(ord($str{$i}) & 0x40); $i--) {
1938                  // find the first byte
1939                  ;
1940              }
1941              if ($i <= 0) {
1942                  return '';
1943              } // sanity check
1944              for ($bc = 0, $mbs = ord($str{$i}); $mbs & 0x80; $mbs = $mbs << 1) {
1945                  // calculate number of bytes
1946                  $bc++;
1947              }
1948              if ($bc + $i > $len) {
1949                  return substr($str, 0, $i);
1950              }
1951              // fallthru: multibyte char fits into length
1952          }
1953          return substr($str, 0, $len);
1954      }
1955  
1956      /**
1957       * Find position of first occurrence of a string, both arguments are in UTF-8.
1958       *
1959       * @param    string        UTF-8 string to search in
1960       * @param    string        UTF-8 string to search for
1961       * @param    integer        Positition to start the search
1962       * @return    integer        The character position
1963       * @see strpos()
1964       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1965       */
1966  	function utf8_strpos($haystack, $needle, $offset = 0) {
1967          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1968              return mb_strpos($haystack, $needle, $offset, 'utf-8');
1969          } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'iconv') {
1970              return iconv_strpos($haystack, $needle, $offset, 'utf-8');
1971          }
1972  
1973          $byte_offset = $this->utf8_char2byte_pos($haystack, $offset);
1974          if ($byte_offset === FALSE) {
1975              return FALSE;
1976          } // offset beyond string length
1977  
1978          $byte_pos = strpos($haystack, $needle, $byte_offset);
1979          if ($byte_pos === FALSE) {
1980              return FALSE;
1981          } // needle not found
1982  
1983          return $this->utf8_byte2char_pos($haystack, $byte_pos);
1984      }
1985  
1986      /**
1987       * Find position of last occurrence of a char in a string, both arguments are in UTF-8.
1988       *
1989       * @param    string        UTF-8 string to search in
1990       * @param    string        UTF-8 character to search for (single character)
1991       * @return    integer        The character position
1992       * @see strrpos()
1993       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
1994       */
1995  	function utf8_strrpos($haystack, $needle) {
1996          if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'mbstring') {
1997              return mb_strrpos($haystack, $needle, 'utf-8');
1998          } elseif ($GLOBALS['TYPO3_CONF_VARS']['SYS']['t3lib_cs_utils'] == 'iconv') {
1999              return iconv_strrpos($haystack, $needle, 'utf-8');
2000          }
2001  
2002          $byte_pos = strrpos($haystack, $needle);
2003          if ($byte_pos === FALSE) {
2004              return FALSE;
2005          } // needle not found
2006  
2007          return $this->utf8_byte2char_pos($haystack, $byte_pos);
2008      }
2009  
2010      /**
2011       * Translates a character position into an 'absolute' byte position.
2012       * Unit tested by Kasper.
2013       *
2014       * @param    string        UTF-8 string
2015       * @param    integer        Character position (negative values start from the end)
2016       * @return    integer        Byte position
2017       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2018       */
2019  	function utf8_char2byte_pos($str, $pos) {
2020          $n = 0; // number of characters found
2021          $p = abs($pos); // number of characters wanted
2022  
2023          if ($pos >= 0) {
2024              $i = 0;
2025              $d = 1;
2026          } else {
2027              $i = strlen($str) - 1;
2028              $d = -1;
2029          }
2030  
2031          for (; strlen($str{$i}) && $n < $p; $i += $d) {
2032              $c = (int) ord($str{$i});
2033              if (!($c & 0x80)) // single-byte (0xxxxxx)
2034              {
2035                  $n++;
2036              }
2037              elseif (($c & 0xC0) == 0xC0) // multi-byte starting byte (11xxxxxx)
2038              {
2039                  $n++;
2040              }
2041          }
2042          if (!strlen($str{$i})) {
2043              return FALSE;
2044          } // offset beyond string length
2045  
2046          if ($pos >= 0) {
2047                  // skip trailing multi-byte data bytes
2048              while ((ord($str{$i}) & 0x80) && !(ord($str{$i}) & 0x40)) {
2049                  $i++;
2050              }
2051          } else {
2052                  // correct offset
2053              $i++;
2054          }
2055  
2056          return $i;
2057      }
2058  
2059      /**
2060       * Translates an 'absolute' byte position into a character position.
2061       * Unit tested by Kasper.
2062       *
2063       * @param    string        UTF-8 string
2064       * @param    integer        byte position
2065       * @return    integer        character position
2066       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2067       */
2068  	function utf8_byte2char_pos($str, $pos) {
2069          $n = 0; // number of characters
2070          for ($i = $pos; $i > 0; $i--) {
2071              $c = (int) ord($str{$i});
2072              if (!($c & 0x80)) // single-byte (0xxxxxx)
2073              {
2074                  $n++;
2075              }
2076              elseif (($c & 0xC0) == 0xC0) // multi-byte starting byte (11xxxxxx)
2077              {
2078                  $n++;
2079              }
2080          }
2081          if (!strlen($str{$i})) {
2082              return FALSE;
2083          } // offset beyond string length
2084  
2085          return $n;
2086      }
2087  
2088      /**
2089       * Maps all characters of an UTF-8 string.
2090       *
2091       * @param    string        UTF-8 string
2092       * @param    string        mode: 'case' (case folding) or 'ascii' (ASCII transliteration)
2093       * @param    string        'case': conversion 'toLower' or 'toUpper'
2094       * @return    string        the converted string
2095       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2096       */
2097  	function utf8_char_mapping($str, $mode, $opt = '') {
2098          if (!$this->initUnicodeData($mode)) {
2099              return $str;
2100          } // do nothing
2101  
2102          $out = '';
2103          switch ($mode) {
2104              case 'case':
2105                  $map =& $this->caseFolding['utf-8'][$opt];
2106                  break;
2107  
2108              case 'ascii':
2109                  $map =& $this->toASCII['utf-8'];
2110                  break;
2111  
2112              default:
2113                  return $str;
2114          }
2115  
2116          for ($i = 0; strlen($str{$i}); $i++) {
2117              $c = ord($str{$i});
2118              if (!($c & 0x80)) // single-byte (0xxxxxx)
2119              {
2120                  $mbc = $str{$i};
2121              }
2122              elseif (($c & 0xC0) == 0xC0) { // multi-byte starting byte (11xxxxxx)
2123                  for ($bc = 0; $c & 0x80; $c = $c << 1) {
2124                      $bc++;
2125                  } // calculate number of bytes
2126                  $mbc = substr($str, $i, $bc);
2127                  $i += $bc - 1;
2128              }
2129  
2130              if (isset($map[$mbc])) {
2131                  $out .= $map[$mbc];
2132              } else {
2133                  $out .= $mbc;
2134              }
2135          }
2136  
2137          return $out;
2138      }
2139  
2140  
2141      /********************************************
2142       *
2143       * Internal EUC string operation functions
2144       *
2145       * Extended Unix Code:
2146       *  ASCII compatible 7bit single bytes chars
2147       *  8bit two byte chars
2148       *
2149       * Shift-JIS is treated as a special case.
2150       *
2151       ********************************************/
2152  
2153      /**
2154       * Cuts a string in the EUC charset family short at a given byte length.
2155       *
2156       * @param    string        EUC multibyte character string
2157       * @param    integer        the byte length
2158       * @param    string        the charset
2159       * @return    string        the shortened string
2160       * @see mb_strcut()
2161       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2162       */
2163  	function euc_strtrunc($str, $len, $charset) {
2164          $sjis = ($charset == 'shift_jis');
2165          for ($i = 0; strlen($str{$i}) && $i < $len; $i++) {
2166              $c = ord($str{$i});
2167              if ($sjis) {
2168                  if (($c >= 0x80 && $c < 0xA0) || ($c >= 0xE0)) {
2169                      $i++;
2170                  } // advance a double-byte char
2171              }
2172              else {
2173                  if ($c >= 0x80) {
2174                      $i++;
2175                  } // advance a double-byte char
2176              }
2177          }
2178          if (!strlen($str{$i})) {
2179              return $str;
2180          } // string shorter than supplied length
2181  
2182          if ($i > $len) {
2183              return substr($str, 0, $len - 1); // we ended on a first byte
2184          } else {
2185              return substr($str, 0, $len);
2186          }
2187      }
2188  
2189      /**
2190       * Returns a part of a string in the EUC charset family.
2191       *
2192       * @param    string        EUC multibyte character string
2193       * @param    integer        start position (character position)
2194       * @param    string        the charset
2195       * @param    integer        length (in characters)
2196       * @return    string        the substring
2197       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2198       */
2199  	function euc_substr($str, $start, $charset, $len = NULL) {
2200          $byte_start = $this->euc_char2byte_pos($str, $start, $charset);
2201          if ($byte_start === FALSE) {
2202              return FALSE;
2203          } // $start outside string length
2204  
2205          $str = substr($str, $byte_start);
2206  
2207          if ($len != NULL) {
2208              $byte_end = $this->euc_char2byte_pos($str, $len, $charset);
2209              if ($byte_end === FALSE) // $len outside actual string length
2210              {
2211                  return $str;
2212              }
2213              else
2214              {
2215                  return substr($str, 0, $byte_end);
2216              }
2217          }
2218          else    {
2219              return $str;
2220          }
2221      }
2222  
2223      /**
2224       * Counts the number of characters of a string in the EUC charset family.
2225       *
2226       * @param    string        EUC multibyte character string
2227       * @param    string        the charset
2228       * @return    integer        the number of characters
2229       * @see strlen()
2230       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2231       */
2232  	function euc_strlen($str, $charset) {
2233          $sjis = ($charset == 'shift_jis');
2234          $n = 0;
2235          for ($i = 0; strlen($str{$i}); $i++) {
2236              $c = ord($str{$i});
2237              if ($sjis) {
2238                  if (($c >= 0x80 && $c < 0xA0) || ($c >= 0xE0)) {
2239                      $i++;
2240                  } // advance a double-byte char
2241              }
2242              else {
2243                  if ($c >= 0x80) {
2244                      $i++;
2245                  } // advance a double-byte char
2246              }
2247  
2248              $n++;
2249          }
2250  
2251          return $n;
2252      }
2253  
2254      /**
2255       * Translates a character position into an 'absolute' byte position.
2256       *
2257       * @param    string        EUC multibyte character string
2258       * @param    integer        character position (negative values start from the end)
2259       * @param    string        the charset
2260       * @return    integer        byte position
2261       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2262       */
2263  	function euc_char2byte_pos($str, $pos, $charset) {
2264          $sjis = ($charset == 'shift_jis');
2265          $n = 0; // number of characters seen
2266          $p = abs($pos); // number of characters wanted
2267  
2268          if ($pos >= 0) {
2269              $i = 0;
2270              $d = 1;
2271          } else {
2272              $i = strlen($str) - 1;
2273              $d = -1;
2274          }
2275  
2276          for (; strlen($str{$i}) && $n < $p; $i += $d) {
2277              $c = ord($str{$i});
2278              if ($sjis) {
2279                  if (($c >= 0x80 && $c < 0xA0) || ($c >= 0xE0)) {
2280                      $i += $d;
2281                  } // advance a double-byte char
2282              }
2283              else {
2284                  if ($c >= 0x80) {
2285                      $i += $d;
2286                  } // advance a double-byte char
2287              }
2288  
2289              $n++;
2290          }
2291          if (!strlen($str{$i})) {
2292              return FALSE;
2293          } // offset beyond string length
2294  
2295          if ($pos < 0) {
2296              $i++;
2297          } // correct offset
2298  
2299          return $i;
2300      }
2301  
2302      /**
2303       * Maps all characters of a string in the EUC charset family.
2304       *
2305       * @param    string        EUC multibyte character string
2306       * @param    string        the charset
2307       * @param    string        mode: 'case' (case folding) or 'ascii' (ASCII transliteration)
2308       * @param    string        'case': conversion 'toLower' or 'toUpper'
2309       * @return    string        the converted string
2310       * @author    Martin Kutschker <martin.t.kutschker@blackbox.net>
2311       */
2312  	function euc_char_mapping($str, $charset, $mode, $opt = '') {
2313          switch ($mode) {
2314              case 'case':
2315                  if (!$this->initCaseFolding($charset)) {
2316                      return $str;
2317                  } // do nothing
2318                  $map =& $this->caseFolding[$charset][$opt];
2319                  break;
2320  
2321              case 'ascii':
2322                  if (!$this->initToASCII($charset)) {
2323                      return $str;
2324                  } // do nothing
2325                  $map =& $this->toASCII[$charset];
2326                  break;
2327  
2328              default:
2329                  return $str;
2330          }
2331  
2332          $sjis = ($charset == 'shift_jis');
2333          $out = '';
2334          for ($i = 0; strlen($str{$i}); $i++) {
2335              $mbc = $str{$i};
2336              $c = ord($mbc);
2337  
2338              if ($sjis) {
2339                  if (($c >= 0x80 && $c < 0xA0) || ($c >= 0xE0)) { // a double-byte char
2340                      $mbc = substr($str, $i, 2);
2341                      $i++;
2342                  }
2343              }
2344              else {
2345                  if ($c >= 0x80) { // a double-byte char
2346                      $mbc = substr($str, $i, 2);
2347                      $i++;
2348                  }
2349              }
2350  
2351              if (isset($map[$mbc])) {
2352                  $out .= $map[$mbc];
2353              } else {
2354                  $out .= $mbc;
2355              }
2356          }
2357  
2358          return $out;
2359      }
2360  
2361  }
2362  
2363  if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_cs.php'])) {
2364      include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_cs.php']);
2365  }
2366  
2367  ?>


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1