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