1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: 22: 23: 24: 25: 26:
27: class String {
28:
29: 30: 31: 32: 33: 34:
35: public static function uuid() {
36: $node = env('SERVER_ADDR');
37:
38: if (strpos($node, ':') !== false) {
39: if (substr_count($node, '::')) {
40: $node = str_replace(
41: '::', str_repeat(':0000', 8 - substr_count($node, ':')) . ':', $node
42: );
43: }
44: $node = explode(':', $node);
45: $ipSix = '';
46:
47: foreach ($node as $id) {
48: $ipSix .= str_pad(base_convert($id, 16, 2), 16, 0, STR_PAD_LEFT);
49: }
50: $node = base_convert($ipSix, 2, 10);
51:
52: if (strlen($node) < 38) {
53: $node = null;
54: } else {
55: $node = crc32($node);
56: }
57: } elseif (empty($node)) {
58: $host = env('HOSTNAME');
59:
60: if (empty($host)) {
61: $host = env('HOST');
62: }
63:
64: if (!empty($host)) {
65: $ip = gethostbyname($host);
66:
67: if ($ip === $host) {
68: $node = crc32($host);
69: } else {
70: $node = ip2long($ip);
71: }
72: }
73: } elseif ($node !== '127.0.0.1') {
74: $node = ip2long($node);
75: } else {
76: $node = null;
77: }
78:
79: if (empty($node)) {
80: $node = crc32(Configure::read('Security.salt'));
81: }
82:
83: if (function_exists('hphp_get_thread_id')) {
84: $pid = hphp_get_thread_id();
85: } elseif (function_exists('zend_thread_id')) {
86: $pid = zend_thread_id();
87: } else {
88: $pid = getmypid();
89: }
90:
91: if (!$pid || $pid > 65535) {
92: $pid = mt_rand(0, 0xfff) | 0x4000;
93: }
94:
95: list($timeMid, $timeLow) = explode(' ', microtime());
96: return sprintf(
97: "%08x-%04x-%04x-%02x%02x-%04x%08x", (int)$timeLow, (int)substr($timeMid, 2) & 0xffff,
98: mt_rand(0, 0xfff) | 0x4000, mt_rand(0, 0x3f) | 0x80, mt_rand(0, 0xff), $pid, $node
99: );
100: }
101:
102: 103: 104: 105: 106: 107: 108: 109: 110: 111:
112: public static function tokenize($data, $separator = ',', $leftBound = '(', $rightBound = ')') {
113: if (empty($data) || is_array($data)) {
114: return $data;
115: }
116:
117: $depth = 0;
118: $offset = 0;
119: $buffer = '';
120: $results = array();
121: $length = strlen($data);
122: $open = false;
123:
124: while ($offset <= $length) {
125: $tmpOffset = -1;
126: $offsets = array(
127: strpos($data, $separator, $offset),
128: strpos($data, $leftBound, $offset),
129: strpos($data, $rightBound, $offset)
130: );
131: for ($i = 0; $i < 3; $i++) {
132: if ($offsets[$i] !== false && ($offsets[$i] < $tmpOffset || $tmpOffset == -1)) {
133: $tmpOffset = $offsets[$i];
134: }
135: }
136: if ($tmpOffset !== -1) {
137: $buffer .= substr($data, $offset, ($tmpOffset - $offset));
138: if (!$depth && $data{$tmpOffset} == $separator) {
139: $results[] = $buffer;
140: $buffer = '';
141: } else {
142: $buffer .= $data{$tmpOffset};
143: }
144: if ($leftBound != $rightBound) {
145: if ($data{$tmpOffset} == $leftBound) {
146: $depth++;
147: }
148: if ($data{$tmpOffset} == $rightBound) {
149: $depth--;
150: }
151: } else {
152: if ($data{$tmpOffset} == $leftBound) {
153: if (!$open) {
154: $depth++;
155: $open = true;
156: } else {
157: $depth--;
158: }
159: }
160: }
161: $offset = ++$tmpOffset;
162: } else {
163: $results[] = $buffer . substr($data, $offset);
164: $offset = $length + 1;
165: }
166: }
167: if (empty($results) && !empty($buffer)) {
168: $results[] = $buffer;
169: }
170:
171: if (!empty($results)) {
172: return array_map('trim', $results);
173: }
174:
175: return array();
176: }
177:
178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198:
199: public static function insert($str, $data, $options = array()) {
200: $defaults = array(
201: 'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false
202: );
203: $options += $defaults;
204: $format = $options['format'];
205: $data = (array)$data;
206: if (empty($data)) {
207: return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
208: }
209:
210: if (!isset($format)) {
211: $format = sprintf(
212: '/(?<!%s)%s%%s%s/',
213: preg_quote($options['escape'], '/'),
214: str_replace('%', '%%', preg_quote($options['before'], '/')),
215: str_replace('%', '%%', preg_quote($options['after'], '/'))
216: );
217: }
218:
219: if (strpos($str, '?') !== false && is_numeric(key($data))) {
220: $offset = 0;
221: while (($pos = strpos($str, '?', $offset)) !== false) {
222: $val = array_shift($data);
223: $offset = $pos + strlen($val);
224: $str = substr_replace($str, $val, $pos, 1);
225: }
226: return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
227: }
228:
229: asort($data);
230:
231: $dataKeys = array_keys($data);
232: $hashKeys = array_map('crc32', $dataKeys);
233: $tempData = array_combine($dataKeys, $hashKeys);
234: krsort($tempData);
235:
236: foreach ($tempData as $key => $hashVal) {
237: $key = sprintf($format, preg_quote($key, '/'));
238: $str = preg_replace($key, $hashVal, $str);
239: }
240: $dataReplacements = array_combine($hashKeys, array_values($data));
241: foreach ($dataReplacements as $tmpHash => $tmpValue) {
242: $tmpValue = (is_array($tmpValue)) ? '' : $tmpValue;
243: $str = str_replace($tmpHash, $tmpValue, $str);
244: }
245:
246: if (!isset($options['format']) && isset($options['before'])) {
247: $str = str_replace($options['escape'] . $options['before'], $options['before'], $str);
248: }
249: return ($options['clean']) ? String::cleanInsert($str, $options) : $str;
250: }
251:
252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262:
263: public static function cleanInsert($str, $options) {
264: $clean = $options['clean'];
265: if (!$clean) {
266: return $str;
267: }
268: if ($clean === true) {
269: $clean = array('method' => 'text');
270: }
271: if (!is_array($clean)) {
272: $clean = array('method' => $options['clean']);
273: }
274: switch ($clean['method']) {
275: case 'html':
276: $clean = array_merge(array(
277: 'word' => '[\w,.]+',
278: 'andText' => true,
279: 'replacement' => '',
280: ), $clean);
281: $kleenex = sprintf(
282: '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
283: preg_quote($options['before'], '/'),
284: $clean['word'],
285: preg_quote($options['after'], '/')
286: );
287: $str = preg_replace($kleenex, $clean['replacement'], $str);
288: if ($clean['andText']) {
289: $options['clean'] = array('method' => 'text');
290: $str = String::cleanInsert($str, $options);
291: }
292: break;
293: case 'text':
294: $clean = array_merge(array(
295: 'word' => '[\w,.]+',
296: 'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
297: 'replacement' => '',
298: ), $clean);
299:
300: $kleenex = sprintf(
301: '/(%s%s%s%s|%s%s%s%s)/',
302: preg_quote($options['before'], '/'),
303: $clean['word'],
304: preg_quote($options['after'], '/'),
305: $clean['gap'],
306: $clean['gap'],
307: preg_quote($options['before'], '/'),
308: $clean['word'],
309: preg_quote($options['after'], '/')
310: );
311: $str = preg_replace($kleenex, $clean['replacement'], $str);
312: break;
313: }
314: return $str;
315: }
316:
317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330:
331: public static function wrap($text, $options = array()) {
332: if (is_numeric($options)) {
333: $options = array('width' => $options);
334: }
335: $options += array('width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0);
336: if ($options['wordWrap']) {
337: $wrapped = wordwrap($text, $options['width'], "\n");
338: } else {
339: $wrapped = trim(chunk_split($text, $options['width'] - 1, "\n"));
340: }
341: if (!empty($options['indent'])) {
342: $chunks = explode("\n", $wrapped);
343: for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) {
344: $chunks[$i] = $options['indent'] . $chunks[$i];
345: }
346: $wrapped = implode("\n", $chunks);
347: }
348: return $wrapped;
349: }
350:
351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366:
367: public static function highlight($text, $phrase, $options = array()) {
368: if (empty($phrase)) {
369: return $text;
370: }
371:
372: $default = array(
373: 'format' => '<span class="highlight">\1</span>',
374: 'html' => false,
375: 'regex' => "|%s|iu"
376: );
377: $options = array_merge($default, $options);
378: extract($options);
379:
380: if (is_array($phrase)) {
381: $replace = array();
382: $with = array();
383:
384: foreach ($phrase as $key => $segment) {
385: $segment = '(' . preg_quote($segment, '|') . ')';
386: if ($html) {
387: $segment = "(?![^<]+>)$segment(?![^<]+>)";
388: }
389:
390: $with[] = (is_array($format)) ? $format[$key] : $format;
391: $replace[] = sprintf($options['regex'], $segment);
392: }
393:
394: return preg_replace($replace, $with, $text);
395: }
396:
397: $phrase = '(' . preg_quote($phrase, '|') . ')';
398: if ($html) {
399: $phrase = "(?![^<]+>)$phrase(?![^<]+>)";
400: }
401:
402: return preg_replace(sprintf($options['regex'], $phrase), $format, $text);
403: }
404:
405: 406: 407: 408: 409: 410: 411:
412: public static function stripLinks($text) {
413: return preg_replace('|<a\s+[^>]+>|im', '', preg_replace('|<\/a>|im', '', $text));
414: }
415:
416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431:
432: public static function tail($text, $length = 100, $options = array()) {
433: $default = array(
434: 'ellipsis' => '...', 'exact' => true
435: );
436: $options = array_merge($default, $options);
437: extract($options);
438:
439: if (!function_exists('mb_strlen')) {
440: class_exists('Multibyte');
441: }
442:
443: if (mb_strlen($text) <= $length) {
444: return $text;
445: }
446:
447: $truncate = mb_substr($text, mb_strlen($text) - $length + mb_strlen($ellipsis));
448: if (!$exact) {
449: $spacepos = mb_strpos($truncate, ' ');
450: $truncate = $spacepos === false ? '' : trim(mb_substr($truncate, $spacepos));
451: }
452:
453: return $ellipsis . $truncate;
454: }
455:
456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473:
474: public static function truncate($text, $length = 100, $options = array()) {
475: $default = array(
476: 'ellipsis' => '...', 'exact' => true, 'html' => false
477: );
478: if (isset($options['ending'])) {
479: $default['ellipsis'] = $options['ending'];
480: } elseif (!empty($options['html']) && Configure::read('App.encoding') === 'UTF-8') {
481: $default['ellipsis'] = "\xe2\x80\xa6";
482: }
483: $options = array_merge($default, $options);
484: extract($options);
485:
486: if (!function_exists('mb_strlen')) {
487: class_exists('Multibyte');
488: }
489:
490: if ($html) {
491: if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
492: return $text;
493: }
494: $totalLength = mb_strlen(strip_tags($ellipsis));
495: $openTags = array();
496: $truncate = '';
497:
498: preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
499: foreach ($tags as $tag) {
500: if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) {
501: if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) {
502: array_unshift($openTags, $tag[2]);
503: } elseif (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) {
504: $pos = array_search($closeTag[1], $openTags);
505: if ($pos !== false) {
506: array_splice($openTags, $pos, 1);
507: }
508: }
509: }
510: $truncate .= $tag[1];
511:
512: $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
513: if ($contentLength + $totalLength > $length) {
514: $left = $length - $totalLength;
515: $entitiesLength = 0;
516: if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) {
517: foreach ($entities[0] as $entity) {
518: if ($entity[1] + 1 - $entitiesLength <= $left) {
519: $left--;
520: $entitiesLength += mb_strlen($entity[0]);
521: } else {
522: break;
523: }
524: }
525: }
526:
527: $truncate .= mb_substr($tag[3], 0, $left + $entitiesLength);
528: break;
529: } else {
530: $truncate .= $tag[3];
531: $totalLength += $contentLength;
532: }
533: if ($totalLength >= $length) {
534: break;
535: }
536: }
537: } else {
538: if (mb_strlen($text) <= $length) {
539: return $text;
540: }
541: $truncate = mb_substr($text, 0, $length - mb_strlen($ellipsis));
542: }
543: if (!$exact) {
544: $spacepos = mb_strrpos($truncate, ' ');
545: if ($html) {
546: $truncateCheck = mb_substr($truncate, 0, $spacepos);
547: $lastOpenTag = mb_strrpos($truncateCheck, '<');
548: $lastCloseTag = mb_strrpos($truncateCheck, '>');
549: if ($lastOpenTag > $lastCloseTag) {
550: preg_match_all('/<[\w]+[^>]*>/s', $truncate, $lastTagMatches);
551: $lastTag = array_pop($lastTagMatches[0]);
552: $spacepos = mb_strrpos($truncate, $lastTag) + mb_strlen($lastTag);
553: }
554: $bits = mb_substr($truncate, $spacepos);
555: preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
556: if (!empty($droppedTags)) {
557: if (!empty($openTags)) {
558: foreach ($droppedTags as $closingTag) {
559: if (!in_array($closingTag[1], $openTags)) {
560: array_unshift($openTags, $closingTag[1]);
561: }
562: }
563: } else {
564: foreach ($droppedTags as $closingTag) {
565: $openTags[] = $closingTag[1];
566: }
567: }
568: }
569: }
570: $truncate = mb_substr($truncate, 0, $spacepos);
571: }
572: $truncate .= $ellipsis;
573:
574: if ($html) {
575: foreach ($openTags as $tag) {
576: $truncate .= '</' . $tag . '>';
577: }
578: }
579:
580: return $truncate;
581: }
582:
583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593:
594: public static function excerpt($text, $phrase, $radius = 100, $ellipsis = '...') {
595: if (empty($text) || empty($phrase)) {
596: return self::truncate($text, $radius * 2, array('ellipsis' => $ellipsis));
597: }
598:
599: $append = $prepend = $ellipsis;
600:
601: $phraseLen = mb_strlen($phrase);
602: $textLen = mb_strlen($text);
603:
604: $pos = mb_strpos(mb_strtolower($text), mb_strtolower($phrase));
605: if ($pos === false) {
606: return mb_substr($text, 0, $radius) . $ellipsis;
607: }
608:
609: $startPos = $pos - $radius;
610: if ($startPos <= 0) {
611: $startPos = 0;
612: $prepend = '';
613: }
614:
615: $endPos = $pos + $phraseLen + $radius;
616: if ($endPos >= $textLen) {
617: $endPos = $textLen;
618: $append = '';
619: }
620:
621: $excerpt = mb_substr($text, $startPos, $endPos - $startPos);
622: $excerpt = $prepend . $excerpt . $append;
623:
624: return $excerpt;
625: }
626:
627: 628: 629: 630: 631: 632: 633: 634: 635:
636: public static function toList($list, $and = 'and', $separator = ', ') {
637: if (count($list) > 1) {
638: return implode($separator, array_slice($list, null, -1)) . ' ' . $and . ' ' . array_pop($list);
639: }
640:
641: return array_pop($list);
642: }
643: }
644: