Overview

Namespaces

  • Liberty
  • None

Classes

  • Liberty\BigInteger
  • Liberty\Block
  • Liberty\Blockchain
  • Liberty\Collection
  • Liberty\CollectionFileList
  • Liberty\File
  • Liberty\Folder
  • Liberty\INI
  • Liberty\LECDSA
  • Liberty\Onecrypt
  • Liberty\SSV
  • Liberty\Text
  • Liberty\Transaction
  • Liberty\Wallet
  • Liberty\WebPeer

Functions

  • __autoload
  • Overview
  • Namespace
  • Class
   1: <?php
   2: 
   3: namespace Liberty;
   4: 
   5: use InvalidArgumentException;
   6: 
   7: 
   8: /**
   9:  * The Text class provide functions to texts and phases.
  10:  * 
  11:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  12:  * 
  13:  * Copyright (c) Liberty Group <cryptolibertygroup@gmail.com>
  14:  *
  15:  * @category  Cryptocurrency
  16:  * @package   Liberty
  17:  * @license   http://www.opensource.org/licenses/mit-license.php MIT License.
  18:  * @version   1.0.0
  19:  * @since     2017-05-14
  20:  * @author    Liberty Group <cryptolibertygroup@gmail.com>
  21:  */
  22: 
  23: 
  24: 
  25: 
  26: 
  27: class Text {
  28: 
  29:     /**
  30:      * The text or string to process.
  31:      * @var string The text to be processed.
  32:      */
  33:     protected $text;
  34: 
  35: 
  36: 
  37:     /**
  38:      * Create a Text object
  39:      * 
  40:      * @param string $string The text or string to process
  41:      * @return boolean true or false
  42:     */
  43:     function __construct($string = "")
  44:     {
  45:         $this->text = $string;
  46:         return true;
  47:     }
  48: 
  49: 
  50: 
  51: 
  52:     function setText($string)
  53:     {
  54:         $this->text = $string;
  55:     }
  56: 
  57: 
  58: 
  59:     function getText()
  60:     {
  61:         return $this->text;
  62:     }
  63: 
  64: 
  65: 
  66:     /**
  67:      * Create a Text from datetime format
  68:      * 
  69:      * @return string String with datetime in text.
  70:      * @param string $lang The Language to be returned in english talk. (Ex: spanish).
  71:     */
  72:     function datetimeToText($lang = "english") {
  73:         if($lang == "english") {
  74:             $datetime = explode(" ", $this->text);
  75:             $date = explode("-", $datetime[0]);
  76:             $this->text = $date[1];
  77:             $month = $this->monthNumToText();
  78:             $string = "$month, $date[2] of $date[0]";
  79:             return $string;
  80:         }
  81:     }
  82: 
  83: 
  84: 
  85:     /**
  86:      * Create a Text from month number
  87:      * 
  88:      * @return string The language returned in english talk. (Ex: spanish).
  89:      * @param string $lang String with month number (Ex: 02).
  90:     */
  91:     function monthNumToText($lang = "english") {
  92:         if($lang == "english") {
  93:             if($this->text == "01") return "january";
  94:             if($this->text == "02") return "february";
  95:             if($this->text == "03") return "march";
  96:             if($this->text == "04") return "april";
  97:             if($this->text == "05") return "may";
  98:             if($this->text == "06") return "june";
  99:             if($this->text == "07") return "july";
 100:             if($this->text == "08") return "august";
 101:             if($this->text == "09") return "september";
 102:             if($this->text == "10") return "october";
 103:             if($this->text == "11") return "november";
 104:             if($this->text == "12") return "december";
 105:         }
 106: 
 107:         if($lang == "spanish") {
 108:             if($this->text == "01") return "enero";
 109:             if($this->text == "02") return "febrero";
 110:             if($this->text == "03") return "marzo";
 111:             if($this->text == "04") return "abril";
 112:             if($this->text == "05") return "mayo";
 113:             if($this->text == "06") return "junio";
 114:             if($this->text == "07") return "julio";
 115:             if($this->text == "08") return "agosto";
 116:             if($this->text == "09") return "septiembre";
 117:             if($this->text == "10") return "octubre";
 118:             if($this->text == "11") return "noviembre";
 119:             if($this->text == "12") return "diciembre";
 120:         }
 121: 
 122:         return "Text::monthNumToText: No Lang Defined";
 123:     }
 124: 
 125: 
 126: 
 127:     /**
 128:      * Limit a phrase at $chars long.
 129:      * 
 130:      * @return string The phrase cut shorted.
 131:      * @param int|string $chars Long of the phrase.
 132:     */
 133:     function cutShort($chars) {
 134:         $length = strlen($this->text);
 135: 
 136:         if ($length > $chars) {
 137:             $start = substr($this->text, 0, $chars);
 138:             $end = substr($this->text, $chars, $length);
 139: 
 140:             $words = explode(" ", $start);
 141:             $lastword = array_pop($words);
 142:             $string = implode(" ", $words);
 143:             $part2 = $lastword . $end;
 144:             
 145:             return $string . "...";
 146:         } else {
 147:             return $string;
 148:         }
 149:     }
 150: 
 151: 
 152: 
 153: 
 154:     /**
 155:      * Generate a random UUID version 4
 156:      *
 157:      * Warning: This method should not be used as a random seed for any cryptographic operations.
 158:      * Instead you should use the openssl or mcrypt extensions.
 159:      *
 160:      * @see http://www.ietf.org/rfc/rfc4122.txt
 161:      * @return string RFC 4122 UUID
 162:      * @copyright Matt Farina MIT License https://github.com/lootils/uuid/blob/master/LICENSE
 163:      */
 164:     public static function uuid()
 165:     {
 166:         return sprintf(
 167:             '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
 168:             // 32 bits for "time_low"
 169:             mt_rand(0, 65535),
 170:             mt_rand(0, 65535),
 171:             // 16 bits for "time_mid"
 172:             mt_rand(0, 65535),
 173:             // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
 174:             mt_rand(0, 4095) | 0x4000,
 175:             // 16 bits, 8 bits for "clk_seq_hi_res",
 176:             // 8 bits for "clk_seq_low",
 177:             // two most significant bits holds zero and one for variant DCE1.1
 178:             mt_rand(0, 0x3fff) | 0x8000,
 179:             // 48 bits for "node"
 180:             mt_rand(0, 65535),
 181:             mt_rand(0, 65535),
 182:             mt_rand(0, 65535)
 183:         );
 184:     }
 185: 
 186: 
 187: 
 188: 
 189: 
 190:         /**
 191:      * Tokenizes a string using $separator, ignoring any instance of $separator that appears between
 192:      * $leftBound and $rightBound.
 193:      *
 194:      * @param string $data The data to tokenize.
 195:      * @param string $separator The token to split the data on.
 196:      * @param string $leftBound The left boundary to ignore separators in.
 197:      * @param string $rightBound The right boundary to ignore separators in.
 198:      * @return array|string Array of tokens in $data or original input if empty.
 199:      */
 200:     public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')')
 201:     {
 202:         if (empty($data)) {
 203:             return [];
 204:         }
 205: 
 206:         $depth = 0;
 207:         $offset = 0;
 208:         $buffer = '';
 209:         $results = [];
 210:         $length = mb_strlen($data);
 211:         $open = false;
 212: 
 213:         while ($offset <= $length) {
 214:             $tmpOffset = -1;
 215:             $offsets = [
 216:                 mb_strpos($data, $separator, $offset),
 217:                 mb_strpos($data, $leftBound, $offset),
 218:                 mb_strpos($data, $rightBound, $offset)
 219:             ];
 220:             for ($i = 0; $i < 3; $i++) {
 221:                 if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
 222:                     $tmpOffset = $offsets[$i];
 223:                 }
 224:             }
 225:             if ($tmpOffset !== -1) {
 226:                 $buffer .= mb_substr($data, $offset, $tmpOffset - $offset);
 227:                 $char = mb_substr($data, $tmpOffset, 1);
 228:                 if (!$depth && $char === $separator) {
 229:                     $results[] = $buffer;
 230:                     $buffer = '';
 231:                 } else {
 232:                     $buffer .= $char;
 233:                 }
 234:                 if ($leftBound !== $rightBound) {
 235:                     if ($char === $leftBound) {
 236:                         $depth++;
 237:                     }
 238:                     if ($char === $rightBound) {
 239:                         $depth--;
 240:                     }
 241:                 } else {
 242:                     if ($char === $leftBound) {
 243:                         if (!$open) {
 244:                             $depth++;
 245:                             $open = true;
 246:                         } else {
 247:                             $depth--;
 248:                             $open = false;
 249:                         }
 250:                     }
 251:                 }
 252:                 $offset = ++$tmpOffset;
 253:             } else {
 254:                 $results[] = $buffer . mb_substr($data, $offset);
 255:                 $offset = $length + 1;
 256:             }
 257:         }
 258:         if (empty($results) && !empty($buffer)) {
 259:             $results[] = $buffer;
 260:         }
 261: 
 262:         if (!empty($results)) {
 263:             return array_map('trim', $results);
 264:         }
 265: 
 266:         return [];
 267:     }
 268: 
 269:     /**
 270:      * Replaces variable placeholders inside a $str with any given $data. Each key in the $data array
 271:      * corresponds to a variable placeholder name in $str.
 272:      * Example:
 273:      * ```
 274:      * Text::insert(':name is :age years old.', ['name' => 'Bob', 'age' => '65']);
 275:      * ```
 276:      * Returns: Bob is 65 years old.
 277:      *
 278:      * Available $options are:
 279:      *
 280:      * - before: The character or string in front of the name of the variable placeholder (Defaults to `:`)
 281:      * - after: The character or string after the name of the variable placeholder (Defaults to null)
 282:      * - escape: The character or string used to escape the before character / string (Defaults to `\`)
 283:      * - format: A regex to use for matching variable placeholders. Default is: `/(?<!\\)\:%s/`
 284:      *   (Overwrites before, after, breaks escape / clean)
 285:      * - clean: A boolean or array with instructions for Text::cleanInsert
 286:      *
 287:      * @param string $str A string containing variable placeholders
 288:      * @param array $data A key => val array where each key stands for a placeholder variable name
 289:      *     to be replaced with val
 290:      * @param array $options An array of options, see description above
 291:      * @return string
 292:      */
 293:     public static function insert($str, $data, array $options = [])
 294:     {
 295:         $defaults = [
 296:             'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false
 297:         ];
 298:         $options += $defaults;
 299:         $format = $options['format'];
 300:         $data = (array)$data;
 301:         if (empty($data)) {
 302:             return $options['clean'] ? static::cleanInsert($str, $options) : $str;
 303:         }
 304: 
 305:         if (!isset($format)) {
 306:             $format = sprintf(
 307:                 '/(?<!%s)%s%%s%s/',
 308:                 preg_quote($options['escape'], '/'),
 309:                 str_replace('%', '%%', preg_quote($options['before'], '/')),
 310:                 str_replace('%', '%%', preg_quote($options['after'], '/'))
 311:             );
 312:         }
 313: 
 314:         if (strpos($str, '?') !== false && is_numeric(key($data))) {
 315:             $offset = 0;
 316:             while (($pos = strpos($str, '?', $offset)) !== false) {
 317:                 $val = array_shift($data);
 318:                 $offset = $pos + strlen($val);
 319:                 $str = substr_replace($str, $val, $pos, 1);
 320:             }
 321: 
 322:             return $options['clean'] ? static::cleanInsert($str, $options) : $str;
 323:         }
 324: 
 325:         asort($data);
 326: 
 327:         $dataKeys = array_keys($data);
 328:         $hashKeys = array_map('crc32', $dataKeys);
 329:         $tempData = array_combine($dataKeys, $hashKeys);
 330:         krsort($tempData);
 331: 
 332:         foreach ($tempData as $key => $hashVal) {
 333:             $key = sprintf($format, preg_quote($key, '/'));
 334:             $str = preg_replace($key, $hashVal, $str);
 335:         }
 336:         $dataReplacements = array_combine($hashKeys, array_values($data));
 337:         foreach ($dataReplacements as $tmpHash => $tmpValue) {
 338:             $tmpValue = is_array($tmpValue) ? '' : $tmpValue;
 339:             $str = str_replace($tmpHash, $tmpValue, $str);
 340:         }
 341: 
 342:         if (!isset($options['format']) && isset($options['before'])) {
 343:             $str = str_replace($options['escape'] . $options['before'], $options['before'], $str);
 344:         }
 345: 
 346:         return $options['clean'] ? static::cleanInsert($str, $options) : $str;
 347:     }
 348: 
 349:     /**
 350:      * Cleans up a Text::insert() formatted string with given $options depending on the 'clean' key in
 351:      * $options. The default method used is text but html is also available. The goal of this function
 352:      * is to replace all whitespace and unneeded markup around placeholders that did not get replaced
 353:      * by Text::insert().
 354:      *
 355:      * @param string $str String to clean.
 356:      * @param array $options Options list.
 357:      * @return string
 358:      * @see \Cake\Utility\Text::insert()
 359:      */
 360:     public static function cleanInsert($str, array $options)
 361:     {
 362:         $clean = $options['clean'];
 363:         if (!$clean) {
 364:             return $str;
 365:         }
 366:         if ($clean === true) {
 367:             $clean = ['method' => 'text'];
 368:         }
 369:         if (!is_array($clean)) {
 370:             $clean = ['method' => $options['clean']];
 371:         }
 372:         switch ($clean['method']) {
 373:             case 'html':
 374:                 $clean += [
 375:                     'word' => '[\w,.]+',
 376:                     'andText' => true,
 377:                     'replacement' => '',
 378:                 ];
 379:                 $kleenex = sprintf(
 380:                     '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
 381:                     preg_quote($options['before'], '/'),
 382:                     $clean['word'],
 383:                     preg_quote($options['after'], '/')
 384:                 );
 385:                 $str = preg_replace($kleenex, $clean['replacement'], $str);
 386:                 if ($clean['andText']) {
 387:                     $options['clean'] = ['method' => 'text'];
 388:                     $str = static::cleanInsert($str, $options);
 389:                 }
 390:                 break;
 391:             case 'text':
 392:                 $clean += [
 393:                     'word' => '[\w,.]+',
 394:                     'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
 395:                     'replacement' => '',
 396:                 ];
 397: 
 398:                 $kleenex = sprintf(
 399:                     '/(%s%s%s%s|%s%s%s%s)/',
 400:                     preg_quote($options['before'], '/'),
 401:                     $clean['word'],
 402:                     preg_quote($options['after'], '/'),
 403:                     $clean['gap'],
 404:                     $clean['gap'],
 405:                     preg_quote($options['before'], '/'),
 406:                     $clean['word'],
 407:                     preg_quote($options['after'], '/')
 408:                 );
 409:                 $str = preg_replace($kleenex, $clean['replacement'], $str);
 410:                 break;
 411:         }
 412: 
 413:         return $str;
 414:     }
 415: 
 416:     /**
 417:      * Wraps text to a specific width, can optionally wrap at word breaks.
 418:      *
 419:      * ### Options
 420:      *
 421:      * - `width` The width to wrap to. Defaults to 72.
 422:      * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
 423:      * - `indent` String to indent with. Defaults to null.
 424:      * - `indentAt` 0 based index to start indenting at. Defaults to 0.
 425:      *
 426:      * @param string $text The text to format.
 427:      * @param array|int $options Array of options to use, or an integer to wrap the text to.
 428:      * @return string Formatted text.
 429:      */
 430:     public static function wrap($text, $options = [])
 431:     {
 432:         if (is_numeric($options)) {
 433:             $options = ['width' => $options];
 434:         }
 435:         $options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
 436:         if ($options['wordWrap']) {
 437:             $wrapped = self::wordWrap($text, $options['width'], "\n");
 438:         } else {
 439:             $wrapped = trim(chunk_split($text, $options['width'] - 1, "\n"));
 440:         }
 441:         if (!empty($options['indent'])) {
 442:             $chunks = explode("\n", $wrapped);
 443:             for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) {
 444:                 $chunks[$i] = $options['indent'] . $chunks[$i];
 445:             }
 446:             $wrapped = implode("\n", $chunks);
 447:         }
 448: 
 449:         return $wrapped;
 450:     }
 451: 
 452:     /**
 453:      * Wraps a complete block of text to a specific width, can optionally wrap
 454:      * at word breaks.
 455:      *
 456:      * ### Options
 457:      *
 458:      * - `width` The width to wrap to. Defaults to 72.
 459:      * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
 460:      * - `indent` String to indent with. Defaults to null.
 461:      * - `indentAt` 0 based index to start indenting at. Defaults to 0.
 462:      *
 463:      * @param string $text The text to format.
 464:      * @param array|int $options Array of options to use, or an integer to wrap the text to.
 465:      * @return string Formatted text.
 466:      */
 467:     public static function wrapBlock($text, $options = [])
 468:     {
 469:         if (is_numeric($options)) {
 470:             $options = ['width' => $options];
 471:         }
 472:         $options += ['width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0];
 473: 
 474:         if (!empty($options['indentAt']) && $options['indentAt'] === 0) {
 475:             $indentLength = !empty($options['indent']) ? strlen($options['indent']) : 0;
 476:             $options['width'] -= $indentLength;
 477: 
 478:             return self::wrap($text, $options);
 479:         }
 480: 
 481:         $wrapped = self::wrap($text, $options);
 482: 
 483:         if (!empty($options['indent'])) {
 484:             $indentationLength = mb_strlen($options['indent']);
 485:             $chunks = explode("\n", $wrapped);
 486:             $count = count($chunks);
 487:             if ($count < 2) {
 488:                 return $wrapped;
 489:             }
 490:             $toRewrap = '';
 491:             for ($i = $options['indentAt']; $i < $count; $i++) {
 492:                 $toRewrap .= mb_substr($chunks[$i], $indentationLength) . ' ';
 493:                 unset($chunks[$i]);
 494:             }
 495:             $options['width'] -= $indentationLength;
 496:             $options['indentAt'] = 0;
 497:             $rewrapped = self::wrap($toRewrap, $options);
 498:             $newChunks = explode("\n", $rewrapped);
 499: 
 500:             $chunks = array_merge($chunks, $newChunks);
 501:             $wrapped = implode("\n", $chunks);
 502:         }
 503: 
 504:         return $wrapped;
 505:     }
 506: 
 507:     /**
 508:      * Unicode and newline aware version of wordwrap.
 509:      *
 510:      * @param string $text The text to format.
 511:      * @param int $width The width to wrap to. Defaults to 72.
 512:      * @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
 513:      * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
 514:      * @return string Formatted text.
 515:      */
 516:     public static function wordWrap($text, $width = 72, $break = "\n", $cut = false)
 517:     {
 518:         $paragraphs = explode($break, $text);
 519:         foreach ($paragraphs as &$paragraph) {
 520:             $paragraph = static::_wordWrap($paragraph, $width, $break, $cut);
 521:         }
 522: 
 523:         return implode($break, $paragraphs);
 524:     }
 525: 
 526:     /**
 527:      * Unicode aware version of wordwrap as helper method.
 528:      *
 529:      * @param string $text The text to format.
 530:      * @param int $width The width to wrap to. Defaults to 72.
 531:      * @param string $break The line is broken using the optional break parameter. Defaults to '\n'.
 532:      * @param bool $cut If the cut is set to true, the string is always wrapped at the specified width.
 533:      * @return string Formatted text.
 534:      */
 535:     protected static function _wordWrap($text, $width = 72, $break = "\n", $cut = false)
 536:     {
 537:         if ($cut) {
 538:             $parts = [];
 539:             while (mb_strlen($text) > 0) {
 540:                 $part = mb_substr($text, 0, $width);
 541:                 $parts[] = trim($part);
 542:                 $text = trim(mb_substr($text, mb_strlen($part)));
 543:             }
 544: 
 545:             return implode($break, $parts);
 546:         }
 547: 
 548:         $parts = [];
 549:         while (mb_strlen($text) > 0) {
 550:             if ($width >= mb_strlen($text)) {
 551:                 $parts[] = trim($text);
 552:                 break;
 553:             }
 554: 
 555:             $part = mb_substr($text, 0, $width);
 556:             $nextChar = mb_substr($text, $width, 1);
 557:             if ($nextChar !== ' ') {
 558:                 $breakAt = mb_strrpos($part, ' ');
 559:                 if ($breakAt === false) {
 560:                     $breakAt = mb_strpos($text, ' ', $width);
 561:                 }
 562:                 if ($breakAt === false) {
 563:                     $parts[] = trim($text);
 564:                     break;
 565:                 }
 566:                 $part = mb_substr($text, 0, $breakAt);
 567:             }
 568: 
 569:             $part = trim($part);
 570:             $parts[] = $part;
 571:             $text = trim(mb_substr($text, mb_strlen($part)));
 572:         }
 573: 
 574:         return implode($break, $parts);
 575:     }
 576: 
 577:     /**
 578:      * Highlights a given phrase in a text. You can specify any expression in highlighter that
 579:      * may include the \1 expression to include the $phrase found.
 580:      *
 581:      * ### Options:
 582:      *
 583:      * - `format` The piece of HTML with that the phrase will be highlighted
 584:      * - `html` If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
 585:      * - `regex` A custom regex rule that is used to match words, default is '|$tag|iu'
 586:      * - `limit` A limit, optional, defaults to -1 (none)
 587:      *
 588:      * @param string $text Text to search the phrase in.
 589:      * @param string|array $phrase The phrase or phrases that will be searched.
 590:      * @param array $options An array of HTML attributes and options.
 591:      * @return string The highlighted text
 592:      * @link http://book.cakephp.org/3.0/en/core-libraries/string.html#highlighting-substrings
 593:      */
 594:     public static function highlight($text, $phrase, array $options = [])
 595:     {
 596:         if (empty($phrase)) {
 597:             return $text;
 598:         }
 599: 
 600:         $defaults = [
 601:             'format' => '<span class="highlight">\1</span>',
 602:             'html' => false,
 603:             'regex' => '|%s|iu',
 604:             'limit' => -1,
 605:         ];
 606:         $options += $defaults;
 607:         $html = $format = $ellipsis = $exact = $limit = null;
 608:         extract($options);
 609: 
 610:         if (is_array($phrase)) {
 611:             $replace = [];
 612:             $with = [];
 613: 
 614:             foreach ($phrase as $key => $segment) {
 615:                 $segment = '(' . preg_quote($segment, '|') . ')';
 616:                 if ($html) {
 617:                     $segment = "(?![^<]+>)$segment(?![^<]+>)";
 618:                 }
 619: 
 620:                 $with[] = is_array($format) ? $format[$key] : $format;
 621:                 $replace[] = sprintf($options['regex'], $segment);
 622:             }
 623: 
 624:             return preg_replace($replace, $with, $text, $limit);
 625:         }
 626: 
 627:         $phrase = '(' . preg_quote($phrase, '|') . ')';
 628:         if ($html) {
 629:             $phrase = "(?![^<]+>)$phrase(?![^<]+>)";
 630:         }
 631: 
 632:         return preg_replace(sprintf($options['regex'], $phrase), $format, $text, $limit);
 633:     }
 634: 
 635:     /**
 636:      * Strips given text of all links (<a href=....).
 637:      *
 638:      * *Warning* This method is not an robust solution in preventing XSS
 639:      * or malicious HTML.
 640:      *
 641:      * @param string $text Text
 642:      * @return string The text without links
 643:      * @deprecated 3.2.12 This method will be removed in 4.0.0
 644:      */
 645:     public static function stripLinks($text)
 646:     {
 647:         do {
 648:             $text = preg_replace('#</?a([/\s][^>]*)?(>|$)#i', '', $text, -1, $count);
 649:         } while ($count);
 650: 
 651:         return $text;
 652:     }
 653: 
 654:     /**
 655:      * Truncates text starting from the end.
 656:      *
 657:      * Cuts a string to the length of $length and replaces the first characters
 658:      * with the ellipsis if the text is longer than length.
 659:      *
 660:      * ### Options:
 661:      *
 662:      * - `ellipsis` Will be used as Beginning and prepended to the trimmed string
 663:      * - `exact` If false, $text will not be cut mid-word
 664:      *
 665:      * @param string $text String to truncate.
 666:      * @param int $length Length of returned string, including ellipsis.
 667:      * @param array $options An array of options.
 668:      * @return string Trimmed string.
 669:      */
 670:     public static function tail($text, $length = 100, array $options = [])
 671:     {
 672:         $default = [
 673:             'ellipsis' => '...', 'exact' => true
 674:         ];
 675:         $options += $default;
 676:         $exact = $ellipsis = null;
 677:         extract($options);
 678: 
 679:         if (mb_strlen($text) <= $length) {
 680:             return $text;
 681:         }
 682: 
 683:         $truncate = mb_substr($text, mb_strlen($text) - $length + mb_strlen($ellipsis));
 684:         if (!$exact) {
 685:             $spacepos = mb_strpos($truncate, ' ');
 686:             $truncate = $spacepos === false ? '' : trim(mb_substr($truncate, $spacepos));
 687:         }
 688: 
 689:         return $ellipsis . $truncate;
 690:     }
 691: 
 692:     /**
 693:      * Truncates text.
 694:      *
 695:      * Cuts a string to the length of $length and replaces the last characters
 696:      * with the ellipsis if the text is longer than length.
 697:      *
 698:      * ### Options:
 699:      *
 700:      * - `ellipsis` Will be used as ending and appended to the trimmed string
 701:      * - `exact` If false, $text will not be cut mid-word
 702:      * - `html` If true, HTML tags would be handled correctly
 703:      * - `trimWidth` If true, $text will be truncated with the width
 704:      *
 705:      * @param string $text String to truncate.
 706:      * @param int $length Length of returned string, including ellipsis.
 707:      * @param array $options An array of HTML attributes and options.
 708:      * @return string Trimmed string.
 709:      * @link http://book.cakephp.org/3.0/en/core-libraries/string.html#truncating-text
 710:      */
 711:     public static function truncate($text, $length = 100, array $options = [])
 712:     {
 713:         $default = [
 714:             'ellipsis' => '...', 'exact' => true, 'html' => false, 'trimWidth' => false,
 715:         ];
 716:         if (!empty($options['html']) && strtolower(mb_internal_encoding()) === 'utf-8') {
 717:             $default['ellipsis'] = "\xe2\x80\xa6";
 718:         }
 719:         $options += $default;
 720: 
 721:         $prefix = '';
 722:         $suffix = $options['ellipsis'];
 723: 
 724:         if ($options['html']) {
 725:             $ellipsisLength = self::_strlen(strip_tags($options['ellipsis']), $options);
 726: 
 727:             $truncateLength = 0;
 728:             $totalLength = 0;
 729:             $openTags = [];
 730:             $truncate = '';
 731: 
 732:             preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
 733:             foreach ($tags as $tag) {
 734:                 $contentLength = self::_strlen($tag[3], $options);
 735: 
 736:                 if ($truncate === '') {
 737:                     if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/i', $tag[2])) {
 738:                         if (preg_match('/<[\w]+[^>]*>/', $tag[0])) {
 739:                             array_unshift($openTags, $tag[2]);
 740:                         } elseif (preg_match('/<\/([\w]+)[^>]*>/', $tag[0], $closeTag)) {
 741:                             $pos = array_search($closeTag[1], $openTags);
 742:                             if ($pos !== false) {
 743:                                 array_splice($openTags, $pos, 1);
 744:                             }
 745:                         }
 746:                     }
 747: 
 748:                     $prefix .= $tag[1];
 749: 
 750:                     if ($totalLength + $contentLength + $ellipsisLength > $length) {
 751:                         $truncate = $tag[3];
 752:                         $truncateLength = $length - $totalLength;
 753:                     } else {
 754:                         $prefix .= $tag[3];
 755:                     }
 756:                 }
 757: 
 758:                 $totalLength += $contentLength;
 759:                 if ($totalLength > $length) {
 760:                     break;
 761:                 }
 762:             }
 763: 
 764:             if ($totalLength <= $length) {
 765:                 return $text;
 766:             }
 767: 
 768:             $text = $truncate;
 769:             $length = $truncateLength;
 770: 
 771:             foreach ($openTags as $tag) {
 772:                 $suffix .= '</' . $tag . '>';
 773:             }
 774:         } else {
 775:             if (self::_strlen($text, $options) <= $length) {
 776:                 return $text;
 777:             }
 778:             $ellipsisLength = self::_strlen($options['ellipsis'], $options);
 779:         }
 780: 
 781:         $result = self::_substr($text, 0, $length - $ellipsisLength, $options);
 782: 
 783:         if (!$options['exact']) {
 784:             if (self::_substr($text, $length - $ellipsisLength, 1, $options) !== ' ') {
 785:                 $result = self::_removeLastWord($result);
 786:             }
 787: 
 788:             // If result is empty, then we don't need to count ellipsis in the cut.
 789:             if (!strlen($result)) {
 790:                 $result = self::_substr($text, 0, $length, $options);
 791:             }
 792:         }
 793: 
 794:         return $prefix . $result . $suffix;
 795:     }
 796: 
 797:     /**
 798:      * Truncate text with specified width.
 799:      *
 800:      * @param string $text String to truncate.
 801:      * @param int $length Length of returned string, including ellipsis.
 802:      * @param array $options An array of HTML attributes and options.
 803:      * @return string Trimmed string.
 804:      * @see \Cake\Utility\Text::truncate()
 805:      */
 806:     public static function truncateByWidth($text, $length = 100, array $options = [])
 807:     {
 808:         return static::truncate($text, $length, ['trimWidth' => true] + $options);
 809:     }
 810: 
 811:     /**
 812:      * Get string length.
 813:      *
 814:      * ### Options:
 815:      *
 816:      * - `html` If true, HTML entities will be handled as decoded characters.
 817:      * - `trimWidth` If true, the width will return.
 818:      *
 819:      * @param string $text The string being checked for length
 820:      * @param array $options An array of options.
 821:      * @return int
 822:      */
 823:     protected static function _strlen($text, array $options)
 824:     {
 825:         if (empty($options['trimWidth'])) {
 826:             $strlen = 'mb_strlen';
 827:         } else {
 828:             $strlen = 'mb_strwidth';
 829:         }
 830: 
 831:         if (empty($options['html'])) {
 832:             return $strlen($text);
 833:         }
 834: 
 835:         $pattern = '/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i';
 836:         $replace = preg_replace_callback(
 837:             $pattern,
 838:             function ($match) use ($strlen) {
 839:                 $utf8 = html_entity_decode($match[0], ENT_HTML5 | ENT_QUOTES, 'UTF-8');
 840: 
 841:                 return str_repeat(' ', $strlen($utf8, 'UTF-8'));
 842:             },
 843:             $text
 844:         );
 845: 
 846:         return $strlen($replace);
 847:     }
 848: 
 849:     /**
 850:      * Return part of a string.
 851:      *
 852:      * ### Options:
 853:      *
 854:      * - `html` If true, HTML entities will be handled as decoded characters.
 855:      * - `trimWidth` If true, will be truncated with specified width.
 856:      *
 857:      * @param string $text The input string.
 858:      * @param int $start The position to begin extracting.
 859:      * @param int $length The desired length.
 860:      * @param array $options An array of options.
 861:      * @return string
 862:      */
 863:     protected static function _substr($text, $start, $length, array $options)
 864:     {
 865:         if (empty($options['trimWidth'])) {
 866:             $substr = 'mb_substr';
 867:         } else {
 868:             $substr = 'mb_strimwidth';
 869:         }
 870: 
 871:         $maxPosition = self::_strlen($text, ['trimWidth' => false] + $options);
 872:         if ($start < 0) {
 873:             $start += $maxPosition;
 874:             if ($start < 0) {
 875:                 $start = 0;
 876:             }
 877:         }
 878:         if ($start >= $maxPosition) {
 879:             return '';
 880:         }
 881: 
 882:         if ($length === null) {
 883:             $length = self::_strlen($text, $options);
 884:         }
 885: 
 886:         if ($length < 0) {
 887:             $text = self::_substr($text, $start, null, $options);
 888:             $start = 0;
 889:             $length += self::_strlen($text, $options);
 890:         }
 891: 
 892:         if ($length <= 0) {
 893:             return '';
 894:         }
 895: 
 896:         if (empty($options['html'])) {
 897:             return (string)$substr($text, $start, $length);
 898:         }
 899: 
 900:         $totalOffset = 0;
 901:         $totalLength = 0;
 902:         $result = '';
 903: 
 904:         $pattern = '/(&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};)/i';
 905:         $parts = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
 906:         foreach ($parts as $part) {
 907:             $offset = 0;
 908: 
 909:             if ($totalOffset < $start) {
 910:                 $len = self::_strlen($part, ['trimWidth' => false] + $options);
 911:                 if ($totalOffset + $len <= $start) {
 912:                     $totalOffset += $len;
 913:                     continue;
 914:                 }
 915: 
 916:                 $offset = $start - $totalOffset;
 917:                 $totalOffset = $start;
 918:             }
 919: 
 920:             $len = self::_strlen($part, $options);
 921:             if ($offset !== 0 || $totalLength + $len > $length) {
 922:                 if (strpos($part, '&') === 0 && preg_match($pattern, $part)
 923:                     && $part !== html_entity_decode($part, ENT_HTML5 | ENT_QUOTES, 'UTF-8')
 924:                 ) {
 925:                     // Entities cannot be passed substr.
 926:                     continue;
 927:                 }
 928: 
 929:                 $part = $substr($part, $offset, $length - $totalLength);
 930:                 $len = self::_strlen($part, $options);
 931:             }
 932: 
 933:             $result .= $part;
 934:             $totalLength += $len;
 935:             if ($totalLength >= $length) {
 936:                 break;
 937:             }
 938:         }
 939: 
 940:         return $result;
 941:     }
 942: 
 943:     /**
 944:      * Removes the last word from the input text.
 945:      *
 946:      * @param string $text The input text
 947:      * @return string
 948:      */
 949:     protected static function _removeLastWord($text)
 950:     {
 951:         $spacepos = mb_strrpos($text, ' ');
 952: 
 953:         if ($spacepos !== false) {
 954:             $lastWord = mb_strrpos($text, $spacepos);
 955: 
 956:             // Some languages are written without word separation.
 957:             // We recognize a string as a word if it doesn't contain any full-width characters.
 958:             if (mb_strwidth($lastWord) === mb_strlen($lastWord)) {
 959:                 $text = mb_substr($text, 0, $spacepos);
 960:             }
 961: 
 962:             return $text;
 963:         }
 964: 
 965:         return '';
 966:     }
 967: 
 968:     /**
 969:      * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side
 970:      * determined by radius.
 971:      *
 972:      * @param string $text String to search the phrase in
 973:      * @param string $phrase Phrase that will be searched for
 974:      * @param int $radius The amount of characters that will be returned on each side of the founded phrase
 975:      * @param string $ellipsis Ending that will be appended
 976:      * @return string Modified string
 977:      * @link http://book.cakephp.org/3.0/en/core-libraries/string.html#extracting-an-excerpt
 978:      */
 979:     public static function excerpt($text, $phrase, $radius = 100, $ellipsis = '...')
 980:     {
 981:         if (empty($text) || empty($phrase)) {
 982:             return static::truncate($text, $radius * 2, ['ellipsis' => $ellipsis]);
 983:         }
 984: 
 985:         $append = $prepend = $ellipsis;
 986: 
 987:         $phraseLen = mb_strlen($phrase);
 988:         $textLen = mb_strlen($text);
 989: 
 990:         $pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase));
 991:         if ($pos === false) {
 992:             return mb_substr($text, 0, $radius) . $ellipsis;
 993:         }
 994: 
 995:         $startPos = $pos - $radius;
 996:         if ($startPos <= 0) {
 997:             $startPos = 0;
 998:             $prepend = '';
 999:         }
1000: 
1001:         $endPos = $pos + $phraseLen + $radius;
1002:         if ($endPos >= $textLen) {
1003:             $endPos = $textLen;
1004:             $append = '';
1005:         }
1006: 
1007:         $excerpt = mb_substr($text, $startPos, $endPos - $startPos);
1008:         $excerpt = $prepend . $excerpt . $append;
1009: 
1010:         return $excerpt;
1011:     }
1012: 
1013:     /**
1014:      * Creates a comma separated list where the last two items are joined with 'and', forming natural language.
1015:      *
1016:      * @param array $list The list to be joined.
1017:      * @param string|null $and The word used to join the last and second last items together with. Defaults to 'and'.
1018:      * @param string $separator The separator used to join all the other items together. Defaults to ', '.
1019:      * @return string The glued together string.
1020:      * @link http://book.cakephp.org/3.0/en/core-libraries/string.html#converting-an-array-to-sentence-form
1021:      */
1022:     public static function toList(array $list, $and = null, $separator = ', ')
1023:     {
1024:         if ($and === null) {
1025:             $and = __d('cake', 'and');
1026:         }
1027:         if (count($list) > 1) {
1028:             return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
1029:         }
1030: 
1031:         return array_pop($list);
1032:     }
1033: 
1034:     /**
1035:      * Check if the string contain multibyte characters
1036:      *
1037:      * @param string $string value to test
1038:      * @return bool
1039:      */
1040:     public static function isMultibyte($string)
1041:     {
1042:         $length = strlen($string);
1043: 
1044:         for ($i = 0; $i < $length; $i++) {
1045:             $value = ord($string[$i]);
1046:             if ($value > 128) {
1047:                 return true;
1048:             }
1049:         }
1050: 
1051:         return false;
1052:     }
1053: 
1054:     /**
1055:      * Converts a multibyte character string
1056:      * to the decimal value of the character
1057:      *
1058:      * @param string $string String to convert.
1059:      * @return array
1060:      */
1061:     public static function utf8($string)
1062:     {
1063:         $map = [];
1064: 
1065:         $values = [];
1066:         $find = 1;
1067:         $length = strlen($string);
1068: 
1069:         for ($i = 0; $i < $length; $i++) {
1070:             $value = ord($string[$i]);
1071: 
1072:             if ($value < 128) {
1073:                 $map[] = $value;
1074:             } else {
1075:                 if (empty($values)) {
1076:                     $find = ($value < 224) ? 2 : 3;
1077:                 }
1078:                 $values[] = $value;
1079: 
1080:                 if (count($values) === $find) {
1081:                     if ($find == 3) {
1082:                         $map[] = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64);
1083:                     } else {
1084:                         $map[] = (($values[0] % 32) * 64) + ($values[1] % 64);
1085:                     }
1086:                     $values = [];
1087:                     $find = 1;
1088:                 }
1089:             }
1090:         }
1091: 
1092:         return $map;
1093:     }
1094: 
1095:     /**
1096:      * Converts the decimal value of a multibyte character string
1097:      * to a string
1098:      *
1099:      * @param array $array Array
1100:      * @return string
1101:      */
1102:     public static function ascii(array $array)
1103:     {
1104:         $ascii = '';
1105: 
1106:         foreach ($array as $utf8) {
1107:             if ($utf8 < 128) {
1108:                 $ascii .= chr($utf8);
1109:             } elseif ($utf8 < 2048) {
1110:                 $ascii .= chr(192 + (($utf8 - ($utf8 % 64)) / 64));
1111:                 $ascii .= chr(128 + ($utf8 % 64));
1112:             } else {
1113:                 $ascii .= chr(224 + (($utf8 - ($utf8 % 4096)) / 4096));
1114:                 $ascii .= chr(128 + ((($utf8 % 4096) - ($utf8 % 64)) / 64));
1115:                 $ascii .= chr(128 + ($utf8 % 64));
1116:             }
1117:         }
1118: 
1119:         return $ascii;
1120:     }
1121: 
1122:     /**
1123:      * Converts filesize from human readable string to bytes
1124:      *
1125:      * @param string $size Size in human readable string like '5MB', '5M', '500B', '50kb' etc.
1126:      * @param mixed $default Value to be returned when invalid size was used, for example 'Unknown type'
1127:      * @return mixed Number of bytes as integer on success, `$default` on failure if not false
1128:      * @throws \InvalidArgumentException On invalid Unit type.
1129:      * @link http://book.cakephp.org/3.0/en/core-libraries/helpers/text.html
1130:      */
1131:     public static function parseFileSize($size, $default = false)
1132:     {
1133:         if (ctype_digit($size)) {
1134:             return (int)$size;
1135:         }
1136:         $size = strtoupper($size);
1137: 
1138:         $l = -2;
1139:         $i = array_search(substr($size, -2), ['KB', 'MB', 'GB', 'TB', 'PB']);
1140:         if ($i === false) {
1141:             $l = -1;
1142:             $i = array_search(substr($size, -1), ['K', 'M', 'G', 'T', 'P']);
1143:         }
1144:         if ($i !== false) {
1145:             $size = substr($size, 0, $l);
1146: 
1147:             return $size * pow(1024, $i + 1);
1148:         }
1149: 
1150:         if (substr($size, -1) === 'B' && ctype_digit(substr($size, 0, -1))) {
1151:             $size = substr($size, 0, -1);
1152: 
1153:             return (int)$size;
1154:         }
1155: 
1156:         if ($default !== false) {
1157:             return $default;
1158:         }
1159:         throw new InvalidArgumentException('No unit type.');
1160:     }
1161: 
1162:     /**
1163:      * Get default transliterator identifier string.
1164:      *
1165:      * @return string Transliterator identifier.
1166:      */
1167:     public static function getTransliteratorId()
1168:     {
1169:         return static::$_defaultTransliteratorId;
1170:     }
1171: 
1172:     /**
1173:      * Set default transliterator identifier string.
1174:      *
1175:      * @param string $transliteratorId Transliterator identifier.
1176:      * @return void
1177:      */
1178:     public static function setTransliteratorId($transliteratorId)
1179:     {
1180:         static::$_defaultTransliteratorId = $transliteratorId;
1181:     }
1182: 
1183:     /**
1184:      * Transliterate string.
1185:      *
1186:      * @param string $string String to transliterate.
1187:      * @param string|null $transliteratorId Transliterator identifier. If null
1188:      *   Text::$_defaultTransliteratorId will be used.
1189:      * @return string
1190:      * @see http://php.net/manual/en/transliterator.transliterate.php
1191:      */
1192:     public static function transliterate($string, $transliteratorId = null)
1193:     {
1194:         $transliteratorId = $transliteratorId ?: static::$_defaultTransliteratorId;
1195: 
1196:         return transliterator_transliterate($transliteratorId, $string);
1197:     }
1198: 
1199:     /**
1200:      * Returns a string with all spaces converted to dashes (by default),
1201:      * characters transliterated to ASCII characters, and non word characters removed.
1202:      *
1203:      * ### Options:
1204:      *
1205:      * - `replacement`: Replacement string. Default '-'.
1206:      * - `transliteratorId`: A valid tranliterator id string.
1207:      *   If default `null` Text::$_defaultTransliteratorId to be used.
1208:      *   If `false` no transliteration will be done, only non words will be removed.
1209:      * - `preserve`: Specific non-word character to preserve. Default `null`.
1210:      *   For e.g. this option can be set to '.' to generate clean file names.
1211:      *
1212:      * @param string $string the string you want to slug
1213:      * @param array $options If string it will be use as replacement character
1214:      *   or an array of options.
1215:      * @return string
1216:      */
1217:     public static function slug($string, $options = [])
1218:     {
1219:         if (is_string($options)) {
1220:             $options = ['replacement' => $options];
1221:         }
1222:         $options += [
1223:             'replacement' => '-',
1224:             'transliteratorId' => null,
1225:             'preserve' => null
1226:         ];
1227: 
1228:         if ($options['transliteratorId'] !== false) {
1229:             $string = static::transliterate($string, $options['transliteratorId']);
1230:         }
1231: 
1232:         $regex = '^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}';
1233:         if ($options['preserve']) {
1234:             $regex .= '(' . preg_quote($options['preserve'], '/') . ')';
1235:         }
1236:         $quotedReplacement = preg_quote($options['replacement'], '/');
1237:         $map = [
1238:             '/[' . $regex . ']/mu' => ' ',
1239:             '/[\s]+/mu' => $options['replacement'],
1240:             sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
1241:         ];
1242:         $string = preg_replace(array_keys($map), $map, $string);
1243: 
1244:         return $string;
1245:     }
1246: 
1247: 
1248:  }
1249: 
API documentation generated by ApiGen