Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Utf8.php

Zuletzt modifiziert: 09.10.2024, 12:57 - Dateigröße: 22.83 KiB


 001  <?php
 002   
 003  /*
 004   * Copyright (C) 2016 Nicolas Grekas - p@tchwork.com
 005   *
 006   * This library is free software; you can redistribute it and/or modify it
 007   * under the terms of the (at your option):
 008   * Apache License v2.0 (http://apache.org/licenses/LICENSE-2.0.txt), or
 009   * GNU General Public License v2.0 (http://gnu.org/licenses/gpl-2.0.txt).
 010   */
 011   
 012  namespace Patchwork;
 013   
 014  use Normalizer as n;
 015   
 016  /**
 017   * UTF-8 Grapheme Cluster aware string manipulations implementing the quasi complete
 018   * set of native PHP string functions that need UTF-8 awareness and more.
 019   * Missing are printf-family functions.
 020   */
 021  class Utf8
 022  {
 023      protected static $pathPrefix;
 024      protected static $commonCaseFold = array(
 025          array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"),
 026          array('μ','s','ι',       'σ','β',       'θ',       'φ',       'π',       'κ',       'ρ',       'ε',       "\xE1\xB9\xA1",'ι'),
 027      );
 028      protected static $cp1252 = array('€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ');
 029      protected static $utf8   = array('€','‚','ƒ','„','…','†','‡','ˆ','‰','Š','‹','Œ','Ž','‘','’','“','”','•','–','—','˜','™','š','›','œ','ž','Ÿ');
 030   
 031      public static function isUtf8($s)
 032      {
 033          return (bool) preg_match('//u', $s); // Since PHP 5.2.5, this also excludes invalid five and six bytes sequences
 034      }
 035   
 036      // Generic UTF-8 to ASCII transliteration
 037   
 038      public static function toAscii($s, $subst_chr = '?')
 039      {
 040          if (preg_match("/[\x80-\xFF]/", $s)) {
 041              static $translitExtra = array();
 042              $translitExtra or $translitExtra = static::getData('translit_extra');
 043   
 044              $s = n::normalize($s, n::NFKC);
 045   
 046              $glibc = 'glibc' === ICONV_IMPL;
 047   
 048              preg_match_all('/./u', $s, $s);
 049   
 050              foreach ($s[0] as &$c) {
 051                  if (!isset($c[1])) {
 052                      continue;
 053                  }
 054   
 055                  if ($glibc) {
 056                      $t = iconv('UTF-8', 'ASCII//TRANSLIT', $c);
 057                  } else {
 058                      $t = iconv('UTF-8', 'ASCII//IGNORE//TRANSLIT', $c);
 059   
 060                      if (!isset($t[0])) {
 061                          $t = '?';
 062                      } elseif (isset($t[1])) {
 063                          $t = ltrim($t, '\'`"^~');
 064                      }
 065                  }
 066   
 067                  if ('?' === $t) {
 068                      if (isset($translitExtra[$c])) {
 069                          $t = $translitExtra[$c];
 070                      } else {
 071                          $t = n::normalize($c, n::NFD);
 072   
 073                          if ($t[0] < "\x80") {
 074                              $t = $t[0];
 075                          } else {
 076                              $t = $subst_chr;
 077                          }
 078                      }
 079                  }
 080   
 081                  $c = $t;
 082              }
 083   
 084              $s = implode('', $s[0]);
 085          }
 086   
 087          return $s;
 088      }
 089   
 090      public static function wrapPath($path = '')
 091      {
 092          if (null === static::$pathPrefix) {
 093              static $hasWfio;
 094              isset($hasWfio) or $hasWfio = extension_loaded('wfio');
 095   
 096              if ($hasWfio) {
 097                  static::$pathPrefix = 'wfio://';
 098              } elseif ('\\' === DIRECTORY_SEPARATOR && class_exists('COM', false)) {
 099                  static::$pathPrefix = 'utf8'.mt_rand();
 100                  stream_wrapper_register(static::$pathPrefix, 'Patchwork\Utf8\WindowsStreamWrapper');
 101                  static::$pathPrefix .= '://';
 102              } else {
 103                  if ('\\' === DIRECTORY_SEPARATOR) {
 104                      trigger_error('The `wfio` or `com_dotnet` extension is required to handle UTF-8 filesystem access on Windows');
 105                  }
 106                  static::$pathPrefix = 'file://';
 107              }
 108          }
 109   
 110          return static::$pathPrefix.$path;
 111      }
 112   
 113      public static function filter($var, $normalization_form = 4 /* n::NFC */, $leading_combining = '◌')
 114      {
 115          switch (gettype($var)) {
 116              case 'array':
 117                  foreach ($var as $k => $v) {
 118                      $var[$k] = static::filter($v, $normalization_form, $leading_combining);
 119                  }
 120                  break;
 121   
 122              case 'object':
 123                  foreach ($var as $k => $v) {
 124                      $var->$k = static::filter($v, $normalization_form, $leading_combining);
 125                  }
 126                  break;
 127   
 128              case 'string':
 129                  if (false !== strpos($var, "\r")) {
 130                      // Workaround https://bugs.php.net/65732
 131                      $var = str_replace("\r\n", "\n", $var);
 132                      $var = strtr($var, "\r", "\n");
 133                  }
 134   
 135                  if (preg_match('/[\x80-\xFF]/', $var)) {
 136                      if (n::isNormalized($var, $normalization_form)) {
 137                          $n = '-';
 138                      } else {
 139                          $n = n::normalize($var, $normalization_form);
 140                          if (isset($n[0])) {
 141                              $var = $n;
 142                          } else {
 143                              $var = static::utf8_encode($var);
 144                          }
 145                      }
 146   
 147                      if ($var[0] >= "\x80" && isset($n[0], $leading_combining[0]) && preg_match('/^\p{Mn}/u', $var)) {
 148                          // Prevent leading combining chars
 149                          // for NFC-safe concatenations.
 150                          $var = $leading_combining.$var;
 151                      }
 152                  }
 153                  break;
 154          }
 155   
 156          return $var;
 157      }
 158   
 159      // Unicode transformation for caseless matching
 160      // see http://unicode.org/reports/tr21/tr21-5.html
 161   
 162      public static function strtocasefold($s, $full = true)
 163      {
 164          $s = str_replace(self::$commonCaseFold[0], self::$commonCaseFold[1], $s);
 165   
 166          if ($full) {
 167              static $fullCaseFold = false;
 168              $fullCaseFold or $fullCaseFold = static::getData('caseFolding_full');
 169   
 170              $s = str_replace($fullCaseFold[0], $fullCaseFold[1], $s);
 171          }
 172   
 173          return static::strtolower($s);
 174      }
 175   
 176      // Generic case sensitive collation support for self::strnatcmp()
 177   
 178      public static function strtonatfold($s)
 179      {
 180          $s = n::normalize($s, n::NFD);
 181   
 182          return preg_replace('/\p{Mn}+/u', '', $s);
 183      }
 184   
 185      // PHP string functions that need UTF-8 awareness
 186   
 187      public static function filter_input($type, $var, $filter = FILTER_DEFAULT, $option = null)
 188      {
 189          if (4 > func_num_args()) {
 190              $var = filter_input($type, $var, $filter);
 191          } else {
 192              $var = filter_input($type, $var, $filter, $option);
 193          }
 194   
 195          return static::filter($var);
 196      }
 197   
 198      public static function filter_input_array($type, $def = null, $add_empty = true)
 199      {
 200          if (2 > func_num_args()) {
 201              $a = filter_input_array($type);
 202          } else {
 203              $a = filter_input_array($type, $def, $add_empty);
 204          }
 205   
 206          return static::filter($a);
 207      }
 208   
 209      public static function json_decode($json, $assoc = false, $depth = 512, $options = 0)
 210      {
 211          if (PHP_VERSION_ID < 50400) {
 212              $json = json_decode($json, $assoc, $depth);
 213          } else {
 214              $json = json_decode($json, $assoc, $depth, $options);
 215          }
 216   
 217          return static::filter($json);
 218      }
 219   
 220      public static function substr($s, $start, $len = 2147483647)
 221      {
 222          static $bug62759;
 223          isset($bug62759) or $bug62759 = extension_loaded('intl') && 'à' === @grapheme_substr('éà', 1, -2);
 224   
 225          if ($bug62759) {
 226              return PHP\Shim\Intl::grapheme_substr_workaround62759($s, $start, $len);
 227          } else {
 228              return grapheme_substr($s, $start, $len);
 229          }
 230      }
 231   
 232      public static function strlen($s)
 233      {
 234          return grapheme_strlen($s);
 235      }
 236      public static function strpos($s, $needle, $offset = 0)
 237      {
 238          // ignore invalid negative offset to keep compatility
 239          // with php < 5.5.35, < 5.6.21, < 7.0.6
 240          return grapheme_strpos($s, $needle, $offset > 0 ? $offset : 0);
 241      }
 242      public static function strrpos($s, $needle, $offset = 0)
 243      {
 244          return grapheme_strrpos($s, $needle, $offset);
 245      }
 246   
 247      public static function stripos($s, $needle, $offset = 0)
 248      {
 249          if (50418 > PHP_VERSION_ID || 50500 == PHP_VERSION_ID) {
 250              // Don't use grapheme_stripos because of https://bugs.php.net/61860
 251              if (!preg_match('//u', $s .= '')) {
 252                  return false;
 253              }
 254              if ($offset < 0) {
 255                  $offset = 0;
 256              }
 257              if (!$needle = mb_stripos($s, $needle .= '', $offset, 'UTF-8')) {
 258                  return $needle;
 259              }
 260   
 261              return grapheme_strlen(iconv_substr($s, 0, $needle, 'UTF-8'));
 262          }
 263   
 264          return grapheme_stripos($s, $needle, $offset);
 265      }
 266   
 267      public static function strripos($s, $needle, $offset = 0)
 268      {
 269          if (50418 > PHP_VERSION_ID || 50500 == PHP_VERSION_ID) {
 270              // Don't use grapheme_strripos because of https://bugs.php.net/61860
 271              if (!preg_match('//u', $s .= '')) {
 272                  return false;
 273              }
 274              if ($offset < 0) {
 275                  $offset = 0;
 276              }
 277              if (!$needle = mb_strripos($s, $needle .= '', $offset, 'UTF-8')) {
 278                  return $needle;
 279              }
 280   
 281              return grapheme_strlen(iconv_substr($s, 0, $needle, 'UTF-8'));
 282          }
 283   
 284          return grapheme_strripos($s, $needle, $offset);
 285      }
 286   
 287      public static function stristr($s, $needle, $before_needle = false)
 288      {
 289          if ('' === $needle .= '') {
 290              return false;
 291          }
 292   
 293          return mb_stristr($s, $needle, $before_needle, 'UTF-8');
 294      }
 295   
 296      public static function strstr($s, $needle, $before_needle = false)
 297      {
 298          return grapheme_strstr($s, $needle, $before_needle);
 299      }
 300      public static function strrchr($s, $needle, $before_needle = false)
 301      {
 302          return mb_strrchr($s, $needle, $before_needle, 'UTF-8');
 303      }
 304      public static function strrichr($s, $needle, $before_needle = false)
 305      {
 306          return mb_strrichr($s, $needle, $before_needle, 'UTF-8');
 307      }
 308   
 309      public static function strtolower($s)
 310      {
 311          return mb_strtolower($s, 'UTF-8');
 312      }
 313      public static function strtoupper($s)
 314      {
 315          return mb_strtoupper($s, 'UTF-8');
 316      }
 317   
 318      public static function wordwrap($s, $width = 75, $break = "\n", $cut = false)
 319      {
 320          if (false === wordwrap('-', $width, $break, $cut)) {
 321              return false;
 322          }
 323   
 324          is_string($break) or $break = (string) $break;
 325   
 326          $w = '';
 327          $s = explode($break, $s);
 328          $iLen = count($s);
 329          $chars = array();
 330   
 331          if (1 === $iLen && '' === $s[0]) {
 332              return '';
 333          }
 334   
 335          for ($i = 0; $i < $iLen; ++$i) {
 336              if ($i) {
 337                  $chars[] = $break;
 338                  $w .= '#';
 339              }
 340   
 341              $c = $s[$i];
 342              unset($s[$i]);
 343   
 344              foreach (self::str_split($c) as $c) {
 345                  $chars[] = $c;
 346                  $w .= ' ' === $c ? ' ' : '?';
 347              }
 348          }
 349   
 350          $s = '';
 351          $j = 0;
 352          $b = $i = -1;
 353          $w = wordwrap($w, $width, '#', $cut);
 354   
 355          while (false !== $b = strpos($w, '#', $b + 1)) {
 356              for (++$i; $i < $b; ++$i) {
 357                  $s .= $chars[$j];
 358                  unset($chars[$j++]);
 359              }
 360   
 361              if ($break === $chars[$j] || ' ' === $chars[$j]) {
 362                  unset($chars[$j++]);
 363              }
 364              $s .= $break;
 365          }
 366   
 367          return $s.implode('', $chars);
 368      }
 369   
 370      public static function chr($c)
 371      {
 372          if (0x80 > $c %= 0x200000) {
 373              return chr($c);
 374          }
 375          if (0x800 > $c) {
 376              return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
 377          }
 378          if (0x10000 > $c) {
 379              return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
 380          }
 381   
 382          return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
 383      }
 384   
 385      public static function count_chars($s, $mode = 0)
 386      {
 387          if (1 != $mode) {
 388              user_error(__METHOD__.'(): the only allowed $mode is 1', E_USER_WARNING);
 389          }
 390          $s = self::str_split($s);
 391   
 392          return array_count_values($s);
 393      }
 394   
 395      public static function ltrim($s, $charlist = null)
 396      {
 397          $charlist = null === $charlist ? '\s' : self::rxClass($charlist);
 398   
 399          return preg_replace("/^{$charlist}+/u", '', $s);
 400      }
 401   
 402      public static function ord($s)
 403      {
 404          $a = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
 405          if (0xF0 <= $a) {
 406              return (($a - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
 407          }
 408          if (0xE0 <= $a) {
 409              return (($a - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
 410          }
 411          if (0xC0 <= $a) {
 412              return (($a - 0xC0) << 6) + $s[2] - 0x80;
 413          }
 414   
 415          return $a;
 416      }
 417   
 418      public static function rtrim($s, $charlist = null)
 419      {
 420          $charlist = null === $charlist ? '\s' : self::rxClass($charlist);
 421   
 422          return preg_replace("/{$charlist}+$/u", '', $s);
 423      }
 424   
 425      public static function trim($s, $charlist = null)
 426      {
 427          return self::rtrim(self::ltrim($s, $charlist), $charlist);
 428      }
 429   
 430      public static function str_ireplace($search, $replace, $subject, &$count = null)
 431      {
 432          $search = (array) $search;
 433   
 434          foreach ($search as $i => $s) {
 435              if ('' === $s .= '') {
 436                  $s = '/^(?<=.)$/';
 437              } else {
 438                  $s = '/'.preg_quote($s, '/').'/ui';
 439              }
 440   
 441              $search[$i] = $s;
 442          }
 443   
 444          $subject = preg_replace($search, $replace, $subject, -1, $replace);
 445          $count = $replace;
 446   
 447          return $subject;
 448      }
 449   
 450      public static function str_pad($s, $len, $pad = ' ', $type = STR_PAD_RIGHT)
 451      {
 452          $slen = grapheme_strlen($s);
 453          if ($len <= $slen) {
 454              return $s;
 455          }
 456   
 457          $padlen = grapheme_strlen($pad);
 458          $freelen = $len - $slen;
 459          $len = $freelen % $padlen;
 460   
 461          if (STR_PAD_RIGHT == $type) {
 462              return $s.str_repeat($pad, $freelen / $padlen).($len ? grapheme_substr($pad, 0, $len) : '');
 463          }
 464          if (STR_PAD_LEFT == $type) {
 465              return str_repeat($pad, $freelen / $padlen).($len ? grapheme_substr($pad, 0, $len) : '').$s;
 466          }
 467          if (STR_PAD_BOTH == $type) {
 468              $freelen /= 2;
 469   
 470              $type = ceil($freelen);
 471              $len = $type % $padlen;
 472              $s .= str_repeat($pad, $type / $padlen).($len ? grapheme_substr($pad, 0, $len) : '');
 473   
 474              $type = floor($freelen);
 475              $len = $type % $padlen;
 476   
 477              return str_repeat($pad, $type / $padlen).($len ? grapheme_substr($pad, 0, $len) : '').$s;
 478          }
 479   
 480          user_error(__METHOD__.'(): Padding type has to be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', E_USER_WARNING);
 481      }
 482   
 483      public static function str_shuffle($s)
 484      {
 485          $s = self::str_split($s);
 486          shuffle($s);
 487   
 488          return implode('', $s);
 489      }
 490   
 491      public static function str_split($s, $len = 1)
 492      {
 493          if (1 > $len = (int) $len) {
 494              $len = func_get_arg(1);
 495   
 496              return str_split($s, $len);
 497          }
 498   
 499          static $hasIntl;
 500          isset($hasIntl) or $hasIntl = extension_loaded('intl');
 501   
 502          if ($hasIntl) {
 503              $a = array();
 504              $p = 0;
 505              $l = strlen($s);
 506   
 507              while ($p < $l) {
 508                  $a[] = grapheme_extract($s, 1, GRAPHEME_EXTR_COUNT, $p, $p);
 509              }
 510          } else {
 511              preg_match_all('/'.GRAPHEME_CLUSTER_RX.'/u', $s, $a);
 512              $a = $a[0];
 513          }
 514   
 515          if (1 == $len) {
 516              return $a;
 517          }
 518   
 519          $s = array();
 520          $p = -1;
 521   
 522          foreach ($a as $l => $a) {
 523              if ($l % $len) {
 524                  $s[$p] .= $a;
 525              } else {
 526                  $s[++$p] = $a;
 527              }
 528          }
 529   
 530          return $s;
 531      }
 532   
 533      public static function str_word_count($s, $format = 0, $charlist = '')
 534      {
 535          $charlist = self::rxClass($charlist, '\pL');
 536          $s = preg_split("/({$charlist}+(?:[\p{Pd}’']{$charlist}+)*)/u", $s, -1, PREG_SPLIT_DELIM_CAPTURE);
 537   
 538          $charlist = array();
 539          $len = count($s);
 540   
 541          if (1 == $format) {
 542              for ($i = 1; $i < $len; $i += 2) {
 543                  $charlist[] = $s[$i];
 544              }
 545          } elseif (2 == $format) {
 546              $offset = grapheme_strlen($s[0]);
 547              for ($i = 1; $i < $len; $i += 2) {
 548                  $charlist[$offset] = $s[$i];
 549                  $offset += grapheme_strlen($s[$i]) + grapheme_strlen($s[$i + 1]);
 550              }
 551          } else {
 552              $charlist = ($len - 1) / 2;
 553          }
 554   
 555          return $charlist;
 556      }
 557   
 558      public static function strcmp($a, $b)
 559      {
 560          return $a.'' === $b.'' ? 0 : strcmp(n::normalize($a, n::NFD), n::normalize($b, n::NFD));
 561      }
 562      public static function strnatcmp($a, $b)
 563      {
 564          return $a.'' === $b.'' ? 0 : strnatcmp(self::strtonatfold($a), self::strtonatfold($b));
 565      }
 566      public static function strcasecmp($a, $b)
 567      {
 568          return self::strcmp(static::strtocasefold($a), static::strtocasefold($b));
 569      }
 570      public static function strnatcasecmp($a, $b)
 571      {
 572          return self::strnatcmp(static::strtocasefold($a), static::strtocasefold($b));
 573      }
 574      public static function strncasecmp($a, $b, $len)
 575      {
 576          return self::strncmp(static::strtocasefold($a), static::strtocasefold($b), $len);
 577      }
 578      public static function strncmp($a, $b, $len)
 579      {
 580          return self::strcmp(self::substr($a, 0, $len), self::substr($b, 0, $len));
 581      }
 582   
 583      public static function strcspn($s, $charlist, $start = 0, $len = 2147483647)
 584      {
 585          if ('' === $charlist .= '') {
 586              return;
 587          }
 588          if ($start || 2147483647 != $len) {
 589              $s = self::substr($s, $start, $len);
 590          }
 591   
 592          return preg_match('/^(.*?)'.self::rxClass($charlist).'/us', $s, $len) ? grapheme_strlen($len[1]) : grapheme_strlen($s);
 593      }
 594   
 595      public static function strpbrk($s, $charlist)
 596      {
 597          if (preg_match('/'.self::rxClass($charlist).'/us', $s, $m)) {
 598              return substr($s, strpos($s, $m[0]));
 599          } else {
 600              return false;
 601          }
 602      }
 603   
 604      public static function strrev($s)
 605      {
 606          $s = self::str_split($s);
 607   
 608          return implode('', array_reverse($s));
 609      }
 610   
 611      public static function strspn($s, $mask, $start = 0, $len = 2147483647)
 612      {
 613          if ($start || 2147483647 != $len) {
 614              $s = self::substr($s, $start, $len);
 615          }
 616   
 617          return preg_match('/^'.self::rxClass($mask).'+/u', $s, $s) ? grapheme_strlen($s[0]) : 0;
 618      }
 619   
 620      public static function strtr($s, $from, $to = null)
 621      {
 622          if (null !== $to) {
 623              $from = self::str_split($from);
 624              $to   = self::str_split($to);
 625   
 626              $a = count($from);
 627              $b = count($to);
 628   
 629              if ($a > $b) {
 630                  $from = array_slice($from, 0, $b);
 631              } elseif ($a < $b) {
 632                  $to   = array_slice($to, 0, $a);
 633              }
 634   
 635              $from = array_combine($from, $to);
 636          }
 637   
 638          return strtr($s, $from);
 639      }
 640   
 641      public static function substr_compare($a, $b, $offset, $len = 2147483647, $i = 0)
 642      {
 643          $a = self::substr($a, $offset, $len);
 644   
 645          return $i ? static::strcasecmp($a, $b) : self::strcmp($a, $b);
 646      }
 647   
 648      public static function substr_count($s, $needle, $offset = 0, $len = 2147483647)
 649      {
 650          return substr_count(self::substr($s, $offset, $len), $needle);
 651      }
 652   
 653      public static function substr_replace($s, $replace, $start, $len = 2147483647)
 654      {
 655          $s       = self::str_split($s);
 656          $replace = self::str_split($replace);
 657          array_splice($s, $start, $len, $replace);
 658   
 659          return implode('', $s);
 660      }
 661   
 662      public static function ucfirst($s)
 663      {
 664          $c = iconv_substr($s, 0, 1, 'UTF-8');
 665   
 666          return static::ucwords($c).substr($s, strlen($c));
 667      }
 668   
 669      public static function lcfirst($s)
 670      {
 671          $c = iconv_substr($s, 0, 1, 'UTF-8');
 672   
 673          return static::strtolower($c).substr($s, strlen($c));
 674      }
 675   
 676      public static function ucwords($s)
 677      {
 678          return preg_replace_callback(
 679              "/\b(.)/u",
 680              function ($matches) {
 681                  return mb_convert_case($matches[1], MB_CASE_TITLE, 'UTF-8');
 682              },
 683              $s
 684          );
 685      }
 686   
 687      public static function number_format($number, $decimals = 0, $dec_point = '.', $thousands_sep = ',')
 688      {
 689          if (PHP_VERSION_ID < 50400) {
 690              if (isset($thousands_sep[1]) || isset($dec_point[1])) {
 691                  return str_replace(
 692                      array('.', ','),
 693                      array($dec_point, $thousands_sep),
 694                      number_format($number, $decimals, '.', ',')
 695                  );
 696              }
 697          }
 698   
 699          return number_format($number, $decimals, $dec_point, $thousands_sep);
 700      }
 701   
 702      public static function utf8_encode($s)
 703      {
 704          $s = utf8_encode($s);
 705          if (false === strpos($s, "\xC2")) {
 706              return $s;
 707          } else {
 708              return str_replace(self::$cp1252, self::$utf8, $s);
 709          }
 710      }
 711   
 712      public static function utf8_decode($s)
 713      {
 714          $s = str_replace(self::$utf8, self::$cp1252, $s);
 715   
 716          return utf8_decode($s);
 717      }
 718   
 719      public static function strwidth($s)
 720      {
 721          if (false !== strpos($s, "\r")) {
 722              $s = str_replace("\r\n", "\n", $s);
 723              $s = strtr($s, "\r", "\n");
 724          }
 725          $width = 0;
 726   
 727          foreach (explode("\n", $s) as $s) {
 728              $s = preg_replace('/\x1B\[[\d;]*m/', '', $s);
 729              $c = substr_count($s, "\xAD") - substr_count($s, "\x08");
 730              $s = preg_replace('/[\x00\x05\x07\p{Mn}\p{Me}\p{Cf}\x{1160}-\x{11FF}\x{200B}]+/u', '', $s);
 731              preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
 732   
 733              if ($width < $c = iconv_strlen($s, 'UTF-8') + $wide + $c) {
 734                  $width = $c;
 735              }
 736          }
 737   
 738          return $width;
 739      }
 740   
 741      protected static function rxClass($s, $class = '')
 742      {
 743          $class = array($class);
 744   
 745          foreach (self::str_split($s) as $s) {
 746              if ('-' === $s) {
 747                  $class[0] = '-'.$class[0];
 748              } elseif (!isset($s[2])) {
 749                  $class[0] .= preg_quote($s, '/');
 750              } elseif (1 === iconv_strlen($s, 'UTF-8')) {
 751                  $class[0] .= $s;
 752              } else {
 753                  $class[] = $s;
 754              }
 755          }
 756   
 757          $class[0] = '['.$class[0].']';
 758   
 759          if (1 === count($class)) {
 760              return $class[0];
 761          } else {
 762              return '(?:'.implode('|', $class).')';
 763          }
 764      }
 765   
 766      protected static function getData($file)
 767      {
 768          $file = __DIR__.'/Utf8/data/'.$file.'.ser';
 769          if (file_exists($file)) {
 770              return unserialize(file_get_contents($file));
 771          } else {
 772              return false;
 773          }
 774      }
 775  }
 776