<?php
function word_limiter( $text, $limit = 30, $chars = '0123456789' ) {
if( strlen( $text ) > $limit ) {
$words = str_word_count( $text, 2, $chars );
$words = array_reverse( $words, TRUE );
foreach( $words as $length => $word ) {
if( $length + strlen( $word ) >= $limit ) {
array_shift( $words );
} else {
break;
}
}
$words = array_reverse( $words );
$text = implode( " ", $words ) . '…';
}
return $text;
}
$str = "Hello this is a list of words that is too long";
echo '1: ' . word_limiter( $str );
$str = "Hello this is a list of words";
echo '2: ' . word_limiter( $str );
?>
Refactorings
No refactoring yet !
DeathfireD
July 31, 2009, July 31, 2009 02:06, permalink
So your making a word limit function? Try this. It's probably not the best method but it's short.
function string_limit_words($string, $word_limit) {
$words = explode(' ', $string);
return implode(' ', array_slice($words, 0, $word_limit));
}
$str_word = "This is a test to see how this function will work.";
$wordlimit = "4";
echo string_limit_words($str_word, $wordlimit);
alex
July 31, 2009, July 31, 2009 06:05, permalink
@DeathfireD
I think what you have created isn't exactly what the OP wanted. He wanted a character limiter, but one that preserved all words.
Here is something I wrote ages ago, so not quite sure if the code quality is too good. Could be useful, at least :)
function abbrString($text, $cutOff) {
if (strlen($text) > $cutOff) {
$text = trim(substr($text, 0 ,$cutOff));
$text = explode(' ', $text);
array_pop($text);
$lastWord = end($text);
while ( ! preg_match('@^[a-zA-Z0-9]*$@i', $lastWord)) {
array_pop($text); // clear off non word
$lastWord = end($text);
}
$text = join(' ', $text);
$text . = '…';
}
return $text;
}
Fanis Hatzidakis
July 31, 2009, July 31, 2009 08:14, permalink
I like your solution, charliefrancis. The 2 array reversings might seem be inefficient but for small strings are not that expensive.
I edited your function to remove them anyway, using end() and key() to work with the words array from the end. I also took advantage of str_word_count which returns the length of the string until the previous word as a key of each word in the array. Do a print_r(str_word_count($text, 2)) to see this.
I'm also including the solution I've been using which doesn't split into words but instead works on the given text as a string. Here I wanted to avoid regular expressions so I went the long way around, sticking substrings together. This one returns an array with all the split parts instead of just trimming the given string. Should be trivial to modify to do that if needed.
function word_limiter_noreverse($text, $limit = 30, $chars = '0123456789') {
if (strlen($text) > $limit) {
$words = str_word_count($text, 2, $chars); //gives you an array of length_until_now => next_word
$numberOfWords = count($words); //or count($words)
$wordIterator = $numberOfWords - 1;
while ($wordIterator > 0) {
end($words) ; //set the array pointer to the last element (this also returns the word but we don't need it
$lengthUntilNow = key($words) ;
if ($lengthUntilNow <= $limit) {
//now $words up until (not including) the last word has length that is under or at $limit
array_pop($words);
break;
}
array_pop($words);
$wordIterator--;
}
$text = implode(" ", $words) . '…';
}
return $text;
}
/**
* Splits a string into multiple parts of <= $maxLength while keeping
* words intact. The resulting array of parts is trimmed of prefix and suffix spaces
*
* If the message passed does not require splitting it will be returned on its own
* in an array
*
* @author Fanis Hatzidakis, code@$surname.org
*
* @param string $msg
* @param int $maxLength
* @return array of parts the string was split in
*/
function splitStringOnWords($msg, $maxLength) {
$msg = trim($msg) ;
$msgLength = mb_strlen($msg);
if ($msgLength <= $maxLength) {
return array($msg);
}
//break it in multiple
$ret = array();
while (mb_strlen($msg, 'UTF-8') > $maxLength) {
$temp = mb_substr($msg, 0, $maxLength);
$msg = mb_substr($msg, $maxLength);
$nextChar = mb_substr($msg, 0, 1);
if ($nextChar !== false && $nextChar != ' ') {
//we split a word in half. Get the left part of the split word and put it back in $msg
$lastSpace = mb_strrpos($temp, ' ');
$lastWordLeftFragment = mb_substr($temp, $lastSpace);
$msg = $lastWordLeftFragment . $msg;
//now cut it off from our max_msg_length sized txt
$temp = mb_substr($temp, 0, $lastSpace);
//remove the ending space
$ret[] = trim($temp);
}
}
$ret[] = trim($msg); //remaining msg is under max_msg_length
return $ret;
}
$lorem300 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse luctus massa sed justo sagittis eget sollicitudin justo ullamcorper. Ut vitae justo libero. Ut in iaculis metus. Vivamus sed nulla libero. Mauris ligula nisl, iaculis in rutrum ut, euismod quis odio. Aliquam vitae felis sed nullam.' ;
$splitWords = splitStringOnWords($lorem300, 60) ;
$resultMsg = 'Split in the following ' . count($splitWords) . ' parts:<br/>' ;
foreach ($splitWords as $part) {
$resultMsg .= chr(34) . $part . chr(34) . ' = ' . strlen($part) . ' chars<br/>' ;
}
echo $resultMsg ;
Morkai
July 31, 2009, July 31, 2009 17:55, permalink
Might want to use prepare_to_trunc() before truncating text.
<?php
function str_trunc($text, $limit, $uniqSeparator = '%%~`~%%')
{
return strlen($text) <= $limit ? $text : (current(explode($uniqSeparator, wordwrap($text, $limit, $uniqSeparator), 2)) . '…');
}
function prepare_to_trunc($text)
{
return preg_replace('/\s{2,}/s', ' ', trim($text));
}
echo "\n", str_trunc('Hello this is a list of words that is too long', 30);
echo "\n", str_trunc('Hello this is a list of words', 30);
?>
charliefrancis
August 10, 2009, August 10, 2009 16:14, permalink
Thanks guys, always good to see people improving my code!
Ruslanas
December 8, 2009, December 08, 2009 23:41, permalink
Not sure which is faster.
<?php
function word_limiter($text, $limit = 30) {
// short enough?
if( strlen($text) <= $limit) {
return $text;
}
// check if last word is full
if( substr($text, $limit, 1) == ' ') {
return trim( substr($text, 0, $limit));
}
// hard cut
$cut = substr($text, 0, $limit);
// remove last word
$words = explode(' ', $cut);
array_pop($words);
return implode(' ', $words);
}
$str = "Hello this is a list of words that is too long";
echo '1: ' . word_limiter($str) . "\n";
$str = "Hello this is a list of words";
echo '2: ' . word_limiter($str);
?>
Connor Williams
December 10, 2011, December 10, 2011 03:09, permalink
Hah, Italy protesters rally against Berlusconi
Gavin Dimattia
December 14, 2011, December 14, 2011 05:53, permalink
Poor news - Syria's 'mutilation mystery' increases...
Cool Code
January 2, 2012, January 02, 2012 15:59, permalink
How can you find the 2 for 1 garlic bread voice chit with regard to lemon fridays?
UK Vouchers
January 12, 2012, January 12, 2012 12:18, permalink
How will you receive the 2 for 1 garlic bread voice chit for citrus fridays?
Coupons
January 25, 2012, January 25, 2012 17:51, permalink
Decent Airline flight Simulators Around to get 2012 this won't break the bank?
Latest Coupons
Yesterday, February 08, 2012 19:49, permalink
How will you deliver joyful 12 months minute card to buddies with myspace?
It returns a string with a certain character limit, but still retaining whole words.
It breaks out of the foreach loop once it has found a string short enough to display, and the character list can be edited.
At the moment it reverses the array and then works through it, effectively going backwards, but would it be better to go forward?