Subversion-Projekte lars-tiefland.cakephp

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* SVN FILE: $Id: text.php 7945 2008-12-19 02:16:01Z gwoo $ */
3
/**
4
 * Text Helper
5
 *
6
 * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
11
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
12
 *
13
 * Licensed under The MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 *
16
 * @filesource
17
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
18
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
19
 * @package       cake
20
 * @subpackage    cake.cake.libs.view.helpers
21
 * @since         CakePHP(tm) v 0.10.0.1076
22
 * @version       $Revision: 7945 $
23
 * @modifiedby    $LastChangedBy: gwoo $
24
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
25
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
26
 */
27
/**
28
 * Included libraries.
29
 *
30
 */
31
if (!class_exists('HtmlHelper')) {
32
	App::import('Helper', 'Html');
33
}
34
if (!class_exists('Multibyte')) {
35
	App::import('Core', 'Multibyte');
36
}
37
/**
38
 * Text helper library.
39
 *
40
 * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
41
 *
42
 * @package       cake
43
 * @subpackage    cake.cake.libs.view.helpers
44
 */
45
class TextHelper extends AppHelper {
46
/**
47
 * Highlights a given phrase in a text. You can specify any expression in highlighter that
48
 * may include the \1 expression to include the $phrase found.
49
 *
50
 * @param string $text Text to search the phrase in
51
 * @param string $phrase The phrase that will be searched
52
 * @param string $highlighter The piece of html with that the phrase will be highlighted
53
 * @param boolean $considerHtml If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
54
 * @return string The highlighted text
55
 * @access public
56
 */
57
	function highlight($text, $phrase, $highlighter = '<span class="highlight">\1</span>', $considerHtml = false) {
58
		if (empty($phrase)) {
59
			return $text;
60
		}
61
 
62
		if (is_array($phrase)) {
63
			$replace = array();
64
			$with = array();
65
 
66
			foreach ($phrase as $key => $value) {
67
				$key = $value;
68
				$value = $highlighter;
69
				$key = '(' . $key . ')';
70
				if ($considerHtml) {
71
					$key = '(?![^<]+>)' . $key . '(?![^<]+>)';
72
				}
73
				$replace[] = '|' . $key . '|iu';
74
				$with[] = empty($value) ? $highlighter : $value;
75
			}
76
 
77
			return preg_replace($replace, $with, $text);
78
		} else {
79
			$phrase = '(' . $phrase . ')';
80
			if ($considerHtml) {
81
				$phrase = '(?![^<]+>)' . $phrase . '(?![^<]+>)';
82
			}
83
 
84
			return preg_replace('|'.$phrase.'|iu', $highlighter, $text);
85
		}
86
	}
87
/**
88
 * Strips given text of all links (<a href=....)
89
 *
90
 * @param string $text Text
91
 * @return string The text without links
92
 * @access public
93
 */
94
	function stripLinks($text) {
95
		return preg_replace('|<a\s+[^>]+>|im', '', preg_replace('|<\/a>|im', '', $text));
96
	}
97
/**
98
 * Adds links (<a href=....) to a given text, by finding text that begins with
99
 * strings like http:// and ftp://.
100
 *
101
 * @param string $text Text to add links to
102
 * @param array $htmlOptions Array of HTML options.
103
 * @return string The text with links
104
 * @access public
105
 */
106
	function autoLinkUrls($text, $htmlOptions = array()) {
107
		$options = 'array(';
108
		foreach ($htmlOptions as $option => $value) {
109
				$value = var_export($value, true);
110
				$options .= "'$option' => $value, ";
111
		}
112
		$options .= ')';
113
 
114
		$text = preg_replace_callback('#(?<!href="|">)((?:http|https|ftp|nntp)://[^ <]+)#i', create_function('$matches',
115
			'$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], $matches[0],' . $options . ');'), $text);
116
 
117
		return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
118
			create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "http://" . strtolower($matches[0]),' . $options . ');'), $text);
119
	}
120
/**
121
 * Adds email links (<a href="mailto:....) to a given text.
122
 *
123
 * @param string $text Text
124
 * @param array $htmlOptions Array of HTML options.
125
 * @return string The text with links
126
 * @access public
127
 */
128
	function autoLinkEmails($text, $htmlOptions = array()) {
129
		$options = 'array(';
130
 
131
		foreach ($htmlOptions as $option => $value) {
132
			$options .= "'$option' => '$value', ";
133
		}
134
		$options .= ')';
135
 
136
		return preg_replace_callback('#([_A-Za-z0-9+-]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#',
137
						create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "mailto:" . $matches[0],' . $options . ');'), $text);
138
	}
139
/**
140
 * Convert all links and email adresses to HTML links.
141
 *
142
 * @param string $text Text
143
 * @param array $htmlOptions Array of HTML options.
144
 * @return string The text with links
145
 * @access public
146
 */
147
	function autoLink($text, $htmlOptions = array()) {
148
		return $this->autoLinkEmails($this->autoLinkUrls($text, $htmlOptions), $htmlOptions);
149
	}
150
/**
151
 * Truncates text.
152
 *
153
 * Cuts a string to the length of $length and replaces the last characters
154
 * with the ending if the text is longer than length.
155
 *
156
 * @param string  $text String to truncate.
157
 * @param integer $length Length of returned string, including ellipsis.
158
 * @param mixed $ending If string, will be used as Ending and appended to the trimmed string. Can also be an associative array that can contain the last three params of this method.
159
 * @param boolean $exact If false, $text will not be cut mid-word
160
 * @param boolean $considerHtml If true, HTML tags would be handled correctly
161
 * @return string Trimmed string.
162
 */
163
	function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) {
164
		if (is_array($ending)) {
165
			extract($ending);
166
		}
167
		if ($considerHtml) {
168
			if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
169
				return $text;
170
			}
171
			$totalLength = mb_strlen($ending);
172
			$openTags = array();
173
			$truncate = '';
174
			preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
175
			foreach ($tags as $tag) {
176
				if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) {
177
					if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) {
178
						array_unshift($openTags, $tag[2]);
179
					} else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) {
180
						$pos = array_search($closeTag[1], $openTags);
181
						if ($pos !== false) {
182
							array_splice($openTags, $pos, 1);
183
						}
184
					}
185
				}
186
				$truncate .= $tag[1];
187
 
188
				$contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
189
				if ($contentLength + $totalLength > $length) {
190
					$left = $length - $totalLength;
191
					$entitiesLength = 0;
192
					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)) {
193
						foreach ($entities[0] as $entity) {
194
							if ($entity[1] + 1 - $entitiesLength <= $left) {
195
								$left--;
196
								$entitiesLength += mb_strlen($entity[0]);
197
							} else {
198
								break;
199
							}
200
						}
201
					}
202
 
203
					$truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength);
204
					break;
205
				} else {
206
					$truncate .= $tag[3];
207
					$totalLength += $contentLength;
208
				}
209
				if ($totalLength >= $length) {
210
					break;
211
				}
212
			}
213
 
214
		} else {
215
			if (mb_strlen($text) <= $length) {
216
				return $text;
217
			} else {
218
				$truncate = mb_substr($text, 0, $length - strlen($ending));
219
			}
220
		}
221
		if (!$exact) {
222
			$spacepos = mb_strrpos($truncate, ' ');
223
			if (isset($spacepos)) {
224
				if ($considerHtml) {
225
					$bits = mb_substr($truncate, $spacepos);
226
					preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
227
					if (!empty($droppedTags)) {
228
						foreach ($droppedTags as $closingTag) {
229
							if (!in_array($closingTag[1], $openTags)) {
230
								array_unshift($openTags, $closingTag[1]);
231
							}
232
						}
233
					}
234
				}
235
				$truncate = mb_substr($truncate, 0, $spacepos);
236
			}
237
		}
238
 
239
		$truncate .= $ending;
240
 
241
		if ($considerHtml) {
242
			foreach ($openTags as $tag) {
243
				$truncate .= '</'.$tag.'>';
244
			}
245
		}
246
 
247
		return $truncate;
248
	}
249
/**
250
 * Alias for truncate().
251
 *
252
 * @see TextHelper::truncate()
253
 * @access public
254
 */
255
	function trim() {
256
		$args = func_get_args();
257
		return call_user_func_array(array(&$this, 'truncate'), $args);
258
	}
259
/**
260
 * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side determined by radius.
261
 *
262
 * @param string $text String to search the phrase in
263
 * @param string $phrase Phrase that will be searched for
264
 * @param integer $radius The amount of characters that will be returned on each side of the founded phrase
265
 * @param string $ending Ending that will be appended
266
 * @return string Modified string
267
 * @access public
268
 */
269
	function excerpt($text, $phrase, $radius = 100, $ending = "...") {
270
		if (empty($text) or empty($phrase)) {
271
			return $this->truncate($text, $radius * 2, $ending);
272
		}
273
 
274
		$phraseLen = strlen($phrase);
275
		if ($radius < $phraseLen) {
276
			$radius = $phraseLen;
277
		}
278
 
279
		$pos = strpos(strtolower($text), strtolower($phrase));
280
		$startPos = 0;
281
		if ($pos > $radius) {
282
			$startPos = $pos - $radius;
283
		}
284
		$textLen = strlen($text);
285
		$endPos = $pos + $phraseLen + $radius;
286
		if ($endPos >= $textLen) {
287
			$endPos = $textLen;
288
		}
289
		$excerpt = substr($text, $startPos, $endPos - $startPos);
290
 
291
		if ($startPos != 0) {
292
			$excerpt = substr_replace($excerpt, $ending, 0, $phraseLen);
293
		}
294
 
295
		if ($endPos != $textLen) {
296
			$excerpt = substr_replace($excerpt, $ending, -$phraseLen);
297
		}
298
 
299
		return $excerpt;
300
	}
301
/**
302
 * Creates a comma separated list where the last two items are joined with 'and', forming natural English
303
 *
304
 * @param array $list The list to be joined
305
 * @return string
306
 * @access public
307
 */
308
	function toList($list, $and = 'and') {
309
		$r = '';
310
		$c = count($list) - 1;
311
		foreach ($list as $i => $item) {
312
			$r .= $item;
313
			if ($c > 0 && $i < $c)
314
			{
315
				$r .= ($i < $c - 1 ? ', ' : " {$and} ");
316
			}
317
		}
318
		return $r;
319
	}
320
/**
321
 * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax.
322
 *
323
 * @param string $text String to "flay"
324
 * @param boolean $allowHtml Set to true if if html is allowed
325
 * @return string "Flayed" text
326
 * @access public
327
 * @todo Change this. We need a real Textile parser.
328
 * @codeCoverageIgnoreStart
329
 */
330
	function flay($text, $allowHtml = false) {
331
		trigger_error(__('(TextHelper::flay) Deprecated: the Flay library is no longer supported and will be removed in a future version.', true), E_USER_WARNING);
332
		if (!class_exists('Flay')) {
333
			uses('flay');
334
		}
335
		return Flay::toHtml($text, false, $allowHtml);
336
	}
337
/**
338
 * @codeCoverageIgnoreEnd
339
 */
340
}
341
?>