1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Latte\Runtime;
9:
10: use Latte;
11:
12:
13: 14: 15: 16: 17:
18: class Filters
19: {
20:
21: public static $dateFormat = '%x';
22:
23:
24: public static $xhtml = FALSE;
25:
26:
27: 28: 29: 30: 31: 32:
33: public static function escapeHtml($s, $quotes = ENT_QUOTES)
34: {
35: if ($s instanceof IHtmlString || $s instanceof \Nette\Utils\IHtmlString) {
36: return $s->__toString(TRUE);
37: }
38: $s = (string) $s;
39: if ($quotes !== ENT_NOQUOTES && strpos($s, '`') !== FALSE && strpbrk($s, ' <>"\'') === FALSE) {
40: $s .= ' ';
41: }
42: return htmlSpecialChars($s, $quotes);
43: }
44:
45:
46: 47: 48: 49: 50:
51: public static function ($s)
52: {
53: $s = (string) $s;
54: if ($s && ($s[0] === '-' || $s[0] === '>' || $s[0] === '!')) {
55: $s = ' ' . $s;
56: }
57: return str_replace('-', '- ', $s);
58: }
59:
60:
61: 62: 63: 64: 65:
66: public static function escapeXML($s)
67: {
68:
69:
70:
71: return htmlSpecialChars(preg_replace('#[\x00-\x08\x0B\x0C\x0E-\x1F]+#', '', $s), ENT_QUOTES);
72: }
73:
74:
75: 76: 77: 78: 79:
80: public static function escapeCss($s)
81: {
82:
83: return addcslashes($s, "\x00..\x1F!\"#$%&'()*+,./:;<=>?@[\\]^`{|}~");
84: }
85:
86:
87: 88: 89: 90: 91:
92: public static function escapeJs($s)
93: {
94: if ($s instanceof IHtmlString || $s instanceof \Nette\Utils\IHtmlString) {
95: $s = $s->__toString(TRUE);
96: }
97:
98: $json = json_encode($s, PHP_VERSION_ID >= 50400 ? JSON_UNESCAPED_UNICODE : 0);
99: if ($error = json_last_error()) {
100: throw new \RuntimeException(PHP_VERSION_ID >= 50500 ? json_last_error_msg() : 'JSON encode error', $error);
101: }
102:
103: return str_replace(array("\xe2\x80\xa8", "\xe2\x80\xa9", ']]>', '<!'), array('\u2028', '\u2029', ']]\x3E', '\x3C!'), $json);
104: }
105:
106:
107: 108: 109: 110: 111:
112: public static function escapeICal($s)
113: {
114:
115: return addcslashes(preg_replace('#[\x00-\x08\x0B\x0C-\x1F]+#', '', $s), "\";\\,:\n");
116: }
117:
118:
119: 120: 121: 122: 123:
124: public static function safeUrl($s)
125: {
126: return preg_match('~^(?:(?:https?|ftp)://[^@]+(?:/.*)?|mailto:.+|[/?#].*|[^:]+)\z~i', $s) ? $s : '';
127: }
128:
129:
130: 131: 132: 133: 134:
135: public static function strip($s)
136: {
137: return preg_replace_callback(
138: '#(</textarea|</pre|</script|^).*?(?=<textarea|<pre|<script|\z)#si',
139: function($m) {
140: return trim(preg_replace('#[ \t\r\n]+#', ' ', $m[0]));
141: },
142: $s
143: );
144: }
145:
146:
147: 148: 149: 150: 151: 152: 153:
154: public static function indent($s, $level = 1, $chars = "\t")
155: {
156: if ($level >= 1) {
157: $s = preg_replace_callback('#<(textarea|pre).*?</\\1#si', function($m) {
158: return strtr($m[0], " \t\r\n", "\x1F\x1E\x1D\x1A");
159: }, $s);
160: if (preg_last_error()) {
161: throw new Latte\RegexpException(NULL, preg_last_error());
162: }
163: $s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
164: $s = strtr($s, "\x1F\x1E\x1D\x1A", " \t\r\n");
165: }
166: return $s;
167: }
168:
169:
170: 171: 172: 173: 174: 175:
176: public static function date($time, $format = NULL)
177: {
178: if ($time == NULL) {
179: return NULL;
180: }
181:
182: if (!isset($format)) {
183: $format = self::$dateFormat;
184: }
185:
186: if ($time instanceof \DateInterval) {
187: return $time->format($format);
188:
189: } elseif (!$time instanceof \DateTime && !$time instanceof \DateTimeInterface) {
190: $time = new \DateTime((is_numeric($time) ? '@' : '') . $time);
191: }
192: return strpos($format, '%') === FALSE
193: ? $time->format($format)
194: : strftime($format, $time->format('U'));
195: }
196:
197:
198: 199: 200: 201: 202: 203:
204: public static function bytes($bytes, $precision = 2)
205: {
206: $bytes = round($bytes);
207: $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
208: foreach ($units as $unit) {
209: if (abs($bytes) < 1024 || $unit === end($units)) {
210: break;
211: }
212: $bytes = $bytes / 1024;
213: }
214: return round($bytes, $precision) . ' ' . $unit;
215: }
216:
217:
218: 219: 220: 221: 222: 223: 224:
225: public static function replace($subject, $search, $replacement = '')
226: {
227: return str_replace($search, $replacement, $subject);
228: }
229:
230:
231: 232: 233: 234: 235: 236:
237: public static function replaceRe($subject, $pattern, $replacement = '')
238: {
239: $res = preg_replace($pattern, $replacement, $subject);
240: if (preg_last_error()) {
241: throw new Latte\RegexpException(NULL, preg_last_error());
242: }
243: return $res;
244: }
245:
246:
247: 248: 249: 250: 251: 252:
253: public static function dataStream($data, $type = NULL)
254: {
255: if ($type === NULL) {
256: $type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
257: }
258: return 'data:' . ($type ? "$type;" : '') . 'base64,' . base64_encode($data);
259: }
260:
261:
262: 263: 264: 265:
266: public static function nl2br($value)
267: {
268: return nl2br($value, self::$xhtml);
269: }
270:
271:
272: 273: 274: 275: 276: 277: 278:
279: public static function substring($s, $start, $length = NULL)
280: {
281: if ($length === NULL) {
282: $length = self::length($s);
283: }
284: if (function_exists('mb_substr')) {
285: return mb_substr($s, $start, $length, 'UTF-8');
286: }
287: return iconv_substr($s, $start, $length, 'UTF-8');
288: }
289:
290:
291: 292: 293: 294: 295: 296: 297:
298: public static function truncate($s, $maxLen, $append = "\xE2\x80\xA6")
299: {
300: if (self::length($s) > $maxLen) {
301: $maxLen = $maxLen - self::length($append);
302: if ($maxLen < 1) {
303: return $append;
304:
305: } elseif (preg_match('#^.{1,'.$maxLen.'}(?=[\s\x00-/:-@\[-`{-~])#us', $s, $matches)) {
306: return $matches[0] . $append;
307:
308: } else {
309: return self::substring($s, 0, $maxLen) . $append;
310: }
311: }
312: return $s;
313: }
314:
315:
316: 317: 318: 319:
320: public static function lower($s)
321: {
322: return mb_strtolower($s, 'UTF-8');
323: }
324:
325:
326: 327: 328: 329:
330: public static function upper($s)
331: {
332: return mb_strtoupper($s, 'UTF-8');
333: }
334:
335:
336: 337: 338: 339:
340: public static function firstUpper($s)
341: {
342: return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1);
343: }
344:
345:
346: 347: 348: 349:
350: public static function capitalize($s)
351: {
352: return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8');
353: }
354:
355:
356: 357: 358: 359:
360: public static function length($s)
361: {
362: return strlen(utf8_decode($s));
363: }
364:
365:
366: 367: 368: 369: 370: 371:
372: public static function trim($s, $charlist = " \t\n\r\0\x0B\xC2\xA0")
373: {
374: $charlist = preg_quote($charlist, '#');
375: $s = preg_replace('#^['.$charlist.']+|['.$charlist.']+\z#u', '', $s);
376: if (preg_last_error()) {
377: throw new Latte\RegexpException(NULL, preg_last_error());
378: }
379: return $s;
380: }
381:
382:
383: 384: 385: 386:
387: public static function htmlAttributes($attrs)
388: {
389: if (!is_array($attrs)) {
390: return '';
391: }
392:
393: $s = '';
394: foreach ($attrs as $key => $value) {
395: if ($value === NULL || $value === FALSE) {
396: continue;
397:
398: } elseif ($value === TRUE) {
399: if (static::$xhtml) {
400: $s .= ' ' . $key . '="' . $key . '"';
401: } else {
402: $s .= ' ' . $key;
403: }
404: continue;
405:
406: } elseif (is_array($value)) {
407: $tmp = NULL;
408: foreach ($value as $k => $v) {
409: if ($v != NULL) {
410:
411: $tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
412: }
413: }
414: if ($tmp === NULL) {
415: continue;
416: }
417:
418: $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
419:
420: } else {
421: $value = (string) $value;
422: }
423:
424: $q = strpos($value, '"') === FALSE ? '"' : "'";
425: $s .= ' ' . $key . '='
426: . $q . str_replace(array('&', $q), array('&', $q === '"' ? '"' : '''), $value)
427: . (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
428: . $q;
429: }
430: return $s;
431: }
432:
433: }
434: