Namespaces

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

Classes

  • Arrays
  • CriticalSection
  • Finder
  • Html
  • Json
  • LimitedScope
  • MimeTypeDetector
  • Neon
  • Paginator
  • Strings

Exceptions

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