Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

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