text.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: text_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 
00004 /**
00005  * Text Helper
00006  *
00007  * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
00008  *
00009  * PHP versions 4 and 5
00010  *
00011  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00012  * Copyright 2005-2008, Cake Software Foundation, Inc.
00013  *                              1785 E. Sahara Avenue, Suite 490-204
00014  *                              Las Vegas, Nevada 89104
00015  *
00016  * Licensed under The MIT License
00017  * Redistributions of files must retain the above copyright notice.
00018  *
00019  * @filesource
00020  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00021  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00022  * @package         cake
00023  * @subpackage      cake.cake.libs.view.helpers
00024  * @since           CakePHP(tm) v 0.10.0.1076
00025  * @version         $Revision: 580 $
00026  * @modifiedby      $LastChangedBy: gwoo $
00027  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00028  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00029  */
00030 
00031 /**
00032  * Included libraries.
00033  *
00034  */
00035 
00036 if (!class_exists('HtmlHelper')) {
00037     App::import('Helper', 'Html');
00038 }
00039 
00040 /**
00041  * Text helper library.
00042  *
00043  * Text manipulations: Highlight, excerpt, truncate, strip of links, convert email addresses to mailto: links...
00044  *
00045  * @package     cake
00046  * @subpackage  cake.cake.libs.view.helpers
00047  */
00048 class TextHelper extends AppHelper {
00049 
00050 /**
00051  * Highlights a given phrase in a text. You can specify any expression in highlighter that
00052  * may include the \1 expression to include the $phrase found.
00053  *
00054  * @param string $text Text to search the phrase in
00055  * @param string $phrase The phrase that will be searched
00056  * @param string $highlighter The piece of html with that the phrase will be highlighted
00057  * @param boolean $considerHtml If true, will ignore any HTML tags, ensuring that only the correct text is highlighted
00058  * @return string The highlighted text
00059  * @access public
00060  */
00061     function highlight($text, $phrase, $highlighter = '<span class="highlight">\1</span>', $considerHtml = false) {
00062         if (empty($phrase)) {
00063             return $text;
00064         }
00065 
00066         if (is_array($phrase)) {
00067             $replace = array();
00068             $with = array();
00069 
00070             foreach ($phrase as $key => $value) {
00071                 $key = $value;
00072                 $value = $highlighter;
00073                 $key = '(' . $key . ')';
00074                 if ($considerHtml) {
00075                     $key = '(?![^<]+>)' . $key . '(?![^<]+>)';
00076                 }
00077                 $replace[] = '|' . $key . '|iu';
00078                 $with[] = empty($value) ? $highlighter : $value;
00079             }
00080             
00081             return preg_replace($replace, $with, $text);
00082         } else {
00083             $phrase = '(' . $phrase . ')';
00084             if ($considerHtml) {
00085                 $phrase = '(?![^<]+>)' . $phrase . '(?![^<]+>)';
00086             }
00087             
00088             return preg_replace('|'.$phrase.'|iu', $highlighter, $text);
00089         }
00090     }
00091 /**
00092  * Strips given text of all links (<a href=....)
00093  *
00094  * @param string $text Text
00095  * @return string The text without links
00096  * @access public
00097  */
00098     function stripLinks($text) {
00099         return preg_replace('|<a\s+[^>]+>|im', '', preg_replace('|<\/a>|im', '', $text));
00100     }
00101 /**
00102  * Adds links (<a href=....) to a given text, by finding text that begins with
00103  * strings like http:// and ftp://.
00104  *
00105  * @param string $text Text to add links to
00106  * @param array $htmlOptions Array of HTML options.
00107  * @return string The text with links
00108  * @access public
00109  */
00110     function autoLinkUrls($text, $htmlOptions = array()) {
00111         $options = 'array(';
00112         foreach ($htmlOptions as $option => $value) {
00113                 $value = var_export($value, true);
00114                 $options .= "'$option' => $value, ";
00115         }
00116         $options .= ')';
00117 
00118         $text = preg_replace_callback('#(?<!href="|">)((?:http|https|ftp|nntp)://[^ <]+)#i', create_function('$matches',
00119             '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], $matches[0],' . $options . ');'), $text);
00120 
00121         return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
00122             create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "http://" . strtolower($matches[0]),' . $options . ');'), $text);
00123     }
00124 /**
00125  * Adds email links (<a href="mailto:....) to a given text.
00126  *
00127  * @param string $text Text
00128  * @param array $htmlOptions Array of HTML options.
00129  * @return string The text with links
00130  * @access public
00131  */
00132     function autoLinkEmails($text, $htmlOptions = array()) {
00133         $options = 'array(';
00134 
00135         foreach ($htmlOptions as $option => $value) {
00136             $options .= "'$option' => '$value', ";
00137         }
00138         $options .= ')';
00139 
00140         return preg_replace_callback('#([_A-Za-z0-9+-]+(?:\.[_A-Za-z0-9+-]+)*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*)#',
00141                         create_function('$matches', '$Html = new HtmlHelper(); $Html->tags = $Html->loadConfig(); return $Html->link($matches[0], "mailto:" . $matches[0],' . $options . ');'), $text);
00142     }
00143 /**
00144  * Convert all links and email adresses to HTML links.
00145  *
00146  * @param string $text Text
00147  * @param array $htmlOptions Array of HTML options.
00148  * @return string The text with links
00149  * @access public
00150  */
00151     function autoLink($text, $htmlOptions = array()) {
00152         return $this->autoLinkEmails($this->autoLinkUrls($text, $htmlOptions), $htmlOptions);
00153     }
00154 /**
00155  * Truncates text.
00156  *
00157  * Cuts a string to the length of $length and replaces the last characters
00158  * with the ending if the text is longer than length.
00159  *
00160  * @param string  $text String to truncate.
00161  * @param integer $length Length of returned string, including ellipsis.
00162  * @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.
00163  * @param boolean $exact If false, $text will not be cut mid-word
00164  * @param boolean $considerHtml If true, HTML tags would be handled correctly
00165  * @return string Trimmed string.
00166  */
00167     function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) {
00168         if (is_array($ending)) {
00169             extract($ending);
00170         }
00171         if ($considerHtml) {
00172             if (strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
00173                 return $text;
00174             }
00175 
00176             preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
00177             $total_length = strlen($ending);
00178             $open_tags = array();
00179             $truncate = '';
00180 
00181             foreach ($lines as $line_matchings) {
00182                 if (!empty($line_matchings[1])) {
00183                     if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
00184                     } elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
00185                         $pos = array_search($tag_matchings[1], $open_tags);
00186                         if ($pos !== false) {
00187                             unset($open_tags[$pos]);
00188                         }
00189                     } elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
00190                         array_unshift($open_tags, strtolower($tag_matchings[1]));
00191                     }
00192                     $truncate .= $line_matchings[1];
00193                 }
00194 
00195                 $content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
00196                 if ($total_length+$content_length > $length) {
00197                     $left = $length - $total_length;
00198                     $entities_length = 0;
00199                     if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
00200                         foreach ($entities[0] as $entity) {
00201                             if ($entity[1]+1-$entities_length <= $left) {
00202                                 $left--;
00203                                 $entities_length += strlen($entity[0]);
00204                             } else {
00205                                 break;
00206                             }
00207                         }
00208                     }
00209                     $truncate .= substr($line_matchings[2], 0, $left+$entities_length);
00210                     break;
00211                 } else {
00212                     $truncate .= $line_matchings[2];
00213                     $total_length += $content_length;
00214                 }
00215 
00216                 if ($total_length >= $length) {
00217                     break;
00218                 }
00219             }
00220         } else {
00221             if (strlen($text) <= $length) {
00222                 return $text;
00223             } else {
00224                 $truncate = substr($text, 0, $length - strlen($ending));
00225             }
00226         }
00227 
00228         if (!$exact) {
00229             $spacepos = strrpos($truncate, ' ');
00230             if (isset($spacepos)) {
00231                 $truncate = substr($truncate, 0, $spacepos);
00232             }
00233         }
00234 
00235         $truncate .= $ending;
00236 
00237         if ($considerHtml) {
00238             foreach ($open_tags as $tag) {
00239                 $truncate .= '</' . $tag . '>';
00240             }
00241         }
00242 
00243         return $truncate;
00244     }
00245 /**
00246  * Alias for truncate().
00247  *
00248  * @see TextHelper::truncate()
00249  * @access public
00250  */
00251     function trim() {
00252         $args = func_get_args();
00253         return call_user_func_array(array(&$this, 'truncate'), $args);
00254     }
00255 /**
00256  * Extracts an excerpt from the text surrounding the phrase with a number of characters on each side determined by radius.
00257  *
00258  * @param string $text String to search the phrase in
00259  * @param string $phrase Phrase that will be searched for
00260  * @param integer $radius The amount of characters that will be returned on each side of the founded phrase
00261  * @param string $ending Ending that will be appended
00262  * @return string Modified string
00263  * @access public
00264  */
00265     function excerpt($text, $phrase, $radius = 100, $ending = "...") {
00266         if (empty($text) or empty($phrase)) {
00267             return $this->truncate($text, $radius * 2, $ending);
00268         }
00269 
00270         if ($radius < strlen($phrase)) {
00271             $radius = strlen($phrase);
00272         }
00273 
00274         $pos = strpos(strtolower($text), strtolower($phrase));
00275         $startPos = ife($pos <= $radius, 0, $pos - $radius);
00276         $endPos = ife($pos + strlen($phrase) + $radius >= strlen($text), strlen($text), $pos + strlen($phrase) + $radius);
00277         $excerpt = substr($text, $startPos, $endPos - $startPos);
00278 
00279         if ($startPos != 0) {
00280             $excerpt = substr_replace($excerpt, $ending, 0, strlen($phrase));
00281         }
00282 
00283         if ($endPos != strlen($text)) {
00284             $excerpt = substr_replace($excerpt, $ending, -strlen($phrase));
00285         }
00286 
00287         return $excerpt;
00288     }
00289 /**
00290  * Creates a comma separated list where the last two items are joined with 'and', forming natural English
00291  *
00292  * @param array $list The list to be joined
00293  * @return string
00294  * @access public
00295  */
00296     function toList($list, $and = 'and') {
00297         $r = '';
00298         $c = count($list) - 1;
00299         foreach ($list as $i => $item) {
00300             $r .= $item;
00301             if ($c > 0 && $i < $c)
00302             {
00303                 $r .= ($i < $c - 1 ? ', ' : " {$and} ");
00304             }
00305         }
00306         return $r;
00307     }
00308 /**
00309  * Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax.
00310  *
00311  * @param string $text String to "flay"
00312  * @param boolean $allowHtml Set to true if if html is allowed
00313  * @return string "Flayed" text
00314  * @access public
00315  * @todo Change this. We need a real Textile parser.
00316  * @codeCoverageIgnoreStart
00317  */
00318     function flay($text, $allowHtml = false) {
00319         trigger_error(__('(TextHelper::flay) Deprecated: the Flay library is no longer supported and will be removed in a future version.', true), E_USER_WARNING);
00320         if (!class_exists('Flay')) {
00321             uses('flay');
00322         }
00323         return Flay::toHtml($text, false, $allowHtml);
00324     }
00325 /**
00326  * @codeCoverageIgnoreEnd
00327  */
00328 }
00329 ?>