Overview

Namespaces

  • Budovy
  • Kdyby
    • BootstrapFormRenderer
      • DI
      • Latte
  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Diagnostics
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
      • Diagnostics
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • PhpGenerator
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
  • NetteModule
  • Nextras
    • Datagrid
  • None
  • PHP
  • Tester
    • CodeCoverage
    • Runner
      • Output
  • Vodacek
    • Forms
      • Controls
  • WebLoader
    • Filter
    • Nette

Classes

  • Arrays
  • Callback
  • FileSystem
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • NeonEntity
  • Paginator
  • Strings
  • Validators

Exceptions

  • AssertionException
  • JsonException
  • NeonException
  • RegexpException
  • TokenizerException
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Utils;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * String tools library.
 15:  *
 16:  * @author     David Grudl
 17:  */
 18: class Strings
 19: {
 20: 
 21:     /**
 22:      * Static class - cannot be instantiated.
 23:      */
 24:     final public function __construct()
 25:     {
 26:         throw new Nette\StaticClassException;
 27:     }
 28: 
 29: 
 30:     /**
 31:      * Checks if the string is valid for the specified encoding.
 32:      * @param  string  byte stream to check
 33:      * @param  string  expected encoding
 34:      * @return bool
 35:      */
 36:     public static function checkEncoding($s, $encoding = 'UTF-8')
 37:     {
 38:         return $s === self::fixEncoding($s, $encoding);
 39:     }
 40: 
 41: 
 42:     /**
 43:      * Returns correctly encoded string.
 44:      * @param  string  byte stream to fix
 45:      * @param  string  encoding
 46:      * @return string
 47:      */
 48:     public static function fixEncoding($s, $encoding = 'UTF-8')
 49:     {
 50:         // removes xD800-xDFFF, x110000 and higher
 51:         if (PHP_VERSION_ID >= 50400) {
 52:             ini_set('mbstring.substitute_character', 'none');
 53:             return mb_convert_encoding($s, $encoding, $encoding);
 54:         } else {
 55:             return @iconv('UTF-16', 'UTF-8//IGNORE', iconv($encoding, 'UTF-16//IGNORE', $s)); // intentionally @
 56:         }
 57:     }
 58: 
 59: 
 60:     /**
 61:      * Returns a specific character.
 62:      * @param  int     codepoint
 63:      * @param  string  encoding
 64:      * @return string
 65:      */
 66:     public static function chr($code, $encoding = 'UTF-8')
 67:     {
 68:         return iconv('UTF-32BE', $encoding . '//IGNORE', pack('N', $code));
 69:     }
 70: 
 71: 
 72:     /**
 73:      * Starts the $haystack string with the prefix $needle?
 74:      * @param  string
 75:      * @param  string
 76:      * @return bool
 77:      */
 78:     public static function startsWith($haystack, $needle)
 79:     {
 80:         return strncmp($haystack, $needle, strlen($needle)) === 0;
 81:     }
 82: 
 83: 
 84:     /**
 85:      * Ends the $haystack string with the suffix $needle?
 86:      * @param  string
 87:      * @param  string
 88:      * @return bool
 89:      */
 90:     public static function endsWith($haystack, $needle)
 91:     {
 92:         return strlen($needle) === 0 || substr($haystack, -strlen($needle)) === $needle;
 93:     }
 94: 
 95: 
 96:     /**
 97:      * Does $haystack contain $needle?
 98:      * @param  string
 99:      * @param  string
100:      * @return bool
101:      */
102:     public static function contains($haystack, $needle)
103:     {
104:         return strpos($haystack, $needle) !== FALSE;
105:     }
106: 
107: 
108:     /**
109:      * Returns a part of UTF-8 string.
110:      * @param  string
111:      * @param  int
112:      * @param  int
113:      * @return string
114:      */
115:     public static function substring($s, $start, $length = NULL)
116:     {
117:         if ($length === NULL) {
118:             $length = self::length($s);
119:         }
120:         if (function_exists('mb_substr')) {
121:             return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
122:         }
123:         return iconv_substr($s, $start, $length, 'UTF-8');
124:     }
125: 
126: 
127:     /**
128:      * Removes special controls characters and normalizes line endings and spaces.
129:      * @param  string  UTF-8 encoding or 8-bit
130:      * @return string
131:      */
132:     public static function normalize($s)
133:     {
134:         $s = self::normalizeNewLines($s);
135: 
136:         // remove control characters; leave \t + \n
137:         $s = preg_replace('#[\x00-\x08\x0B-\x1F\x7F]+#', '', $s);
138: 
139:         // right trim
140:         $s = preg_replace('#[\t ]+$#m', '', $s);
141: 
142:         // leading and trailing blank lines
143:         $s = trim($s, "\n");
144: 
145:         return $s;
146:     }
147: 
148: 
149:     /**
150:      * Standardize line endings to unix-like.
151:      * @param  string  UTF-8 encoding or 8-bit
152:      * @return string
153:      */
154:     public static function normalizeNewLines($s)
155:     {
156:         return str_replace(array("\r\n", "\r"), "\n", $s);
157:     }
158: 
159: 
160:     /**
161:      * Converts to ASCII.
162:      * @param  string  UTF-8 encoding
163:      * @return string  ASCII
164:      */
165:     public static function toAscii($s)
166:     {
167:         $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
168:         $s = strtr($s, '`\'"^~', "\x01\x02\x03\x04\x05");
169:         if (ICONV_IMPL === 'glibc') {
170:             $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT', $s); // intentionally @
171:             $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
172:                 . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
173:                 . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
174:                 . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96",
175:                 "ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt-");
176:         } else {
177:             $s = @iconv('UTF-8', 'ASCII//TRANSLIT', $s); // intentionally @
178:         }
179:         $s = str_replace(array('`', "'", '"', '^', '~'), '', $s);
180:         return strtr($s, "\x01\x02\x03\x04\x05", '`\'"^~');
181:     }
182: 
183: 
184:     /**
185:      * Converts to web safe characters [a-z0-9-] text.
186:      * @param  string  UTF-8 encoding
187:      * @param  string  allowed characters
188:      * @param  bool
189:      * @return string
190:      */
191:     public static function webalize($s, $charlist = NULL, $lower = TRUE)
192:     {
193:         $s = self::toAscii($s);
194:         if ($lower) {
195:             $s = strtolower($s);
196:         }
197:         $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
198:         $s = trim($s, '-');
199:         return $s;
200:     }
201: 
202: 
203:     /**
204:      * Truncates string to maximal length.
205:      * @param  string  UTF-8 encoding
206:      * @param  int
207:      * @param  string  UTF-8 encoding
208:      * @return string
209:      */
210:     public static function truncate($s, $maxLen, $append = "\xE2\x80\xA6")
211:     {
212:         if (self::length($s) > $maxLen) {
213:             $maxLen = $maxLen - self::length($append);
214:             if ($maxLen < 1) {
215:                 return $append;
216: 
217:             } elseif ($matches = self::match($s, '#^.{1,'.$maxLen.'}(?=[\s\x00-/:-@\[-`{-~])#us')) {
218:                 return $matches[0] . $append;
219: 
220:             } else {
221:                 return self::substring($s, 0, $maxLen) . $append;
222:             }
223:         }
224:         return $s;
225:     }
226: 
227: 
228:     /**
229:      * Indents the content from the left.
230:      * @param  string  UTF-8 encoding or 8-bit
231:      * @param  int
232:      * @param  string
233:      * @return string
234:      */
235:     public static function indent($s, $level = 1, $chars = "\t")
236:     {
237:         if ($level > 0) {
238:             $s = self::replace($s, '#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level));
239:         }
240:         return $s;
241:     }
242: 
243: 
244:     /**
245:      * Convert to lower case.
246:      * @param  string  UTF-8 encoding
247:      * @return string
248:      */
249:     public static function lower($s)
250:     {
251:         return mb_strtolower($s, 'UTF-8');
252:     }
253: 
254: 
255:     /**
256:      * Convert to upper case.
257:      * @param  string  UTF-8 encoding
258:      * @return string
259:      */
260:     public static function upper($s)
261:     {
262:         return mb_strtoupper($s, 'UTF-8');
263:     }
264: 
265: 
266:     /**
267:      * Convert first character to upper case.
268:      * @param  string  UTF-8 encoding
269:      * @return string
270:      */
271:     public static function firstUpper($s)
272:     {
273:         return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1);
274:     }
275: 
276: 
277:     /**
278:      * Capitalize string.
279:      * @param  string  UTF-8 encoding
280:      * @return string
281:      */
282:     public static function capitalize($s)
283:     {
284:         return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8');
285:     }
286: 
287: 
288:     /**
289:      * Case-insensitive compares UTF-8 strings.
290:      * @param  string
291:      * @param  string
292:      * @param  int
293:      * @return bool
294:      */
295:     public static function compare($left, $right, $len = NULL)
296:     {
297:         if ($len < 0) {
298:             $left = self::substring($left, $len, -$len);
299:             $right = self::substring($right, $len, -$len);
300:         } elseif ($len !== NULL) {
301:             $left = self::substring($left, 0, $len);
302:             $right = self::substring($right, 0, $len);
303:         }
304:         return self::lower($left) === self::lower($right);
305:     }
306: 
307: 
308:     /**
309:      * Finds the length of common prefix of strings.
310:      * @param  string|array
311:      * @param  string
312:      * @return string
313:      */
314:     public static function findPrefix($strings, $second = NULL)
315:     {
316:         if (!is_array($strings)) {
317:             $strings = func_get_args();
318:         }
319:         $first = array_shift($strings);
320:         for ($i = 0; $i < strlen($first); $i++) {
321:             foreach ($strings as $s) {
322:                 if (!isset($s[$i]) || $first[$i] !== $s[$i]) {
323:                     while ($i && $first[$i-1] >= "\x80" && $first[$i] >= "\x80" && $first[$i] < "\xC0") {
324:                         $i--;
325:                     }
326:                     return substr($first, 0, $i);
327:                 }
328:             }
329:         }
330:         return $first;
331:     }
332: 
333: 
334:     /**
335:      * Returns UTF-8 string length.
336:      * @param  string
337:      * @return int
338:      */
339:     public static function length($s)
340:     {
341:         return strlen(utf8_decode($s)); // fastest way
342:     }
343: 
344: 
345:     /**
346:      * Strips whitespace.
347:      * @param  string  UTF-8 encoding
348:      * @param  string
349:      * @return string
350:      */
351:     public static function trim($s, $charlist = " \t\n\r\0\x0B\xC2\xA0")
352:     {
353:         $charlist = preg_quote($charlist, '#');
354:         return self::replace($s, '#^['.$charlist.']+|['.$charlist.']+\z#u', '');
355:     }
356: 
357: 
358:     /**
359:      * Pad a string to a certain length with another string.
360:      * @param  string  UTF-8 encoding
361:      * @param  int
362:      * @param  string
363:      * @return string
364:      */
365:     public static function padLeft($s, $length, $pad = ' ')
366:     {
367:         $length = max(0, $length - self::length($s));
368:         $padLen = self::length($pad);
369:         return str_repeat($pad, $length / $padLen) . self::substring($pad, 0, $length % $padLen) . $s;
370:     }
371: 
372: 
373:     /**
374:      * Pad a string to a certain length with another string.
375:      * @param  string  UTF-8 encoding
376:      * @param  int
377:      * @param  string
378:      * @return string
379:      */
380:     public static function padRight($s, $length, $pad = ' ')
381:     {
382:         $length = max(0, $length - self::length($s));
383:         $padLen = self::length($pad);
384:         return $s . str_repeat($pad, $length / $padLen) . self::substring($pad, 0, $length % $padLen);
385:     }
386: 
387: 
388:     /**
389:      * Reverse string.
390:      * @param  string  UTF-8 encoding
391:      * @return string
392:      */
393:     public static function reverse($s)
394:     {
395:         return @iconv('UTF-32LE', 'UTF-8', strrev(@iconv('UTF-8', 'UTF-32BE', $s)));
396:     }
397: 
398: 
399:     /**
400:      * Generate random string.
401:      * @param  int
402:      * @param  string
403:      * @return string
404:      */
405:     public static function random($length = 10, $charlist = '0-9a-z')
406:     {
407:         $charlist = str_shuffle(preg_replace_callback('#.-.#', function($m) {
408:             return implode('', range($m[0][0], $m[0][2]));
409:         }, $charlist));
410:         $chLen = strlen($charlist);
411: 
412:         if (function_exists('openssl_random_pseudo_bytes')
413:             && (PHP_VERSION_ID >= 50400 || !defined('PHP_WINDOWS_VERSION_BUILD')) // slow in PHP 5.3 & Windows
414:         ) {
415:             $rand3 = openssl_random_pseudo_bytes($length);
416:         }
417:         if (empty($rand3) && function_exists('mcrypt_create_iv')) {
418:             $rand3 = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
419:         }
420:         if (empty($rand3) && @is_readable('/dev/urandom')) {
421:             $rand3 = file_get_contents('/dev/urandom', FALSE, NULL, -1, $length);
422:         }
423:         if (empty($rand3)) {
424:             static $cache;
425:             $rand3 = $cache ?: $cache = md5(serialize($_SERVER), TRUE);
426:         }
427: 
428:         $s = '';
429:         for ($i = 0; $i < $length; $i++) {
430:             if ($i % 5 === 0) {
431:                 list($rand, $rand2) = explode(' ', microtime());
432:                 $rand += lcg_value();
433:             }
434:             $rand *= $chLen;
435:             $s .= $charlist[($rand + $rand2 + ord($rand3[$i % strlen($rand3)])) % $chLen];
436:             $rand -= (int) $rand;
437:         }
438:         return $s;
439:     }
440: 
441: 
442:     /**
443:      * Splits string by a regular expression.
444:      * @param  string
445:      * @param  string
446:      * @param  int
447:      * @return array
448:      */
449:     public static function split($subject, $pattern, $flags = 0)
450:     {
451:         set_error_handler(function($severity, $message) use ($pattern) { // preg_last_error does not return compile errors
452:             restore_error_handler();
453:             throw new RegexpException("$message in pattern: $pattern");
454:         });
455:         $res = preg_split($pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE);
456:         restore_error_handler();
457:         if (preg_last_error()) { // run-time error
458:             throw new RegexpException(NULL, preg_last_error(), $pattern);
459:         }
460:         return $res;
461:     }
462: 
463: 
464:     /**
465:      * Performs a regular expression match.
466:      * @param  string
467:      * @param  string
468:      * @param  int  can be PREG_OFFSET_CAPTURE (returned in bytes)
469:      * @param  int  offset in bytes
470:      * @return mixed
471:      */
472:     public static function match($subject, $pattern, $flags = 0, $offset = 0)
473:     {
474:         if ($offset > strlen($subject)) {
475:             return NULL;
476:         }
477:         set_error_handler(function($severity, $message) use ($pattern) { // preg_last_error does not return compile errors
478:             restore_error_handler();
479:             throw new RegexpException("$message in pattern: $pattern");
480:         });
481:         $res = preg_match($pattern, $subject, $m, $flags, $offset);
482:         restore_error_handler();
483:         if (preg_last_error()) { // run-time error
484:             throw new RegexpException(NULL, preg_last_error(), $pattern);
485:         }
486:         if ($res) {
487:             return $m;
488:         }
489:     }
490: 
491: 
492:     /**
493:      * Performs a global regular expression match.
494:      * @param  string
495:      * @param  string
496:      * @param  int  can be PREG_OFFSET_CAPTURE (returned in bytes); PREG_SET_ORDER is default
497:      * @param  int  offset in bytes
498:      * @return array
499:      */
500:     public static function matchAll($subject, $pattern, $flags = 0, $offset = 0)
501:     {
502:         if ($offset > strlen($subject)) {
503:             return array();
504:         }
505:         set_error_handler(function($severity, $message) use ($pattern) { // preg_last_error does not return compile errors
506:             restore_error_handler();
507:             throw new RegexpException("$message in pattern: $pattern");
508:         });
509:         preg_match_all(
510:             $pattern, $subject, $m,
511:             ($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
512:             $offset
513:         );
514:         restore_error_handler();
515:         if (preg_last_error()) { // run-time error
516:             throw new RegexpException(NULL, preg_last_error(), $pattern);
517:         }
518:         return $m;
519:     }
520: 
521: 
522:     /**
523:      * Perform a regular expression search and replace.
524:      * @param  string
525:      * @param  string|array
526:      * @param  string|callable
527:      * @param  int
528:      * @return string
529:      */
530:     public static function replace($subject, $pattern, $replacement = NULL, $limit = -1)
531:     {
532:         if (is_object($replacement) || is_array($replacement)) {
533:             if ($replacement instanceof Nette\Callback) {
534:                 $replacement = $replacement->getNative();
535:             }
536:             if (!is_callable($replacement, FALSE, $textual)) {
537:                 throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
538:             }
539: 
540:             set_error_handler(function($severity, $message) use (& $tmp) { // preg_last_error does not return compile errors
541:                 restore_error_handler();
542:                 throw new RegexpException("$message in pattern: $tmp");
543:             });
544:             foreach ((array) $pattern as $tmp) {
545:                 preg_match($tmp, '');
546:             }
547:             restore_error_handler();
548: 
549:             $res = preg_replace_callback($pattern, $replacement, $subject, $limit);
550:             if ($res === NULL && preg_last_error()) { // run-time error
551:                 throw new RegexpException(NULL, preg_last_error(), $pattern);
552:             }
553:             return $res;
554: 
555:         } elseif ($replacement === NULL && is_array($pattern)) {
556:             $replacement = array_values($pattern);
557:             $pattern = array_keys($pattern);
558:         }
559: 
560:         set_error_handler(function($severity, $message) use ($pattern) { // preg_last_error does not return compile errors
561:             restore_error_handler();
562:             throw new RegexpException("$message in pattern: " . implode(' or ', (array) $pattern));
563:         });
564:         $res = preg_replace($pattern, $replacement, $subject, $limit);
565:         restore_error_handler();
566:         if (preg_last_error()) { // run-time error
567:             throw new RegexpException(NULL, preg_last_error(), implode(' or ', (array) $pattern));
568:         }
569:         return $res;
570:     }
571: 
572: }
573: 
574: 
575: /**
576:  * The exception that indicates error of the last Regexp execution.
577:  */
578: class RegexpException extends \Exception
579: {
580:     static public $messages = array(
581:         PREG_INTERNAL_ERROR => 'Internal error',
582:         PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
583:         PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
584:         PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
585:         5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
586:     );
587: 
588:     public function __construct($message, $code = NULL, $pattern = NULL)
589:     {
590:         if (!$message) {
591:             $message = (isset(self::$messages[$code]) ? self::$messages[$code] : 'Unknown error') . ($pattern ? " (pattern: $pattern)" : '');
592:         }
593:         parent::__construct($message, $code);
594:     }
595: 
596: }
597: 
API documentation generated by ApiGen 2.8.0