Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

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