Limit text words without cut last word - php

Im showing a text excerpt that starts with a searched word with more 35 chars after that searched word.
Do you know some way to show this text excerpt (searched word + 35chars) without cut the last word, because with substr is not working?
$search = $url[1];
$read = $pdo->prepare("SELECT * FROM pages WHERE title LIKE ? OR content LIKE ? LIMIT ?,?");
$read->bindValue(1, "%$search%", PDO::PARAM_STR);
$read->bindValue(2, "%$search%", PDO::PARAM_STR);
$read->bindParam(3, $begin,PDO::PARAM_INT);
$read->bindParam(4, $max,PDO::PARAM_INT);
$read->execute();
$searchPos = stripos($result['content'],$search);
$searchLen = strlen($search);
$result_text = '"'.substr($result['content'], $searchPos, $searchLen + 35).'..."';
echo '<p>'.strip_tags($result_text).'</p>';

I'm guessing you are looking for something like the following:
<?php
#Do you know some way to show this text excerpt (searched word + 35chars) without cut the last word?
$search = 'Do';
$str = explode(' ', 'you know some way to show this text excerpt (searched word + 35chars) without cut the last word?');
$len = strlen($search) + 1;#might need to be 0 if you don\'t want search to be with the 35 characters
$ret = $search . ' ';
foreach($str as $st){
if(strlen($st) + $len < 35){
$len += strlen($st) + 1;
$ret .= $st . ' ';
}else{
break;
}
}
$ret = trim($ret);
var_dump($ret);
which gives string(33) "Do you know some way to show this"

Please use something like this:
//$orgText = "This text is exactly the same length...";
//$orgText = "This text is shorter...";
//$orgText = "This_text_has_no_spaces_and_is_shorter";
//$orgText = "This_text_has_no_spaces_and_is_the_same";
//$orgText = "This_text_has_no_spaces_and_is_really_longer";
$orgText = "This text is longer and will be definitely cut but last word survives...";
$searchedWord = "This";
$charsToShow = 35;
$desiredExcerptLength = strlen($searchedWord) + $charsToShow;
$searchedWordPos = strpos($orgText, $searchedWord);
if ($searchedWordPos !== false) { // Word found
$subText = substr($orgText, $searchedWordPos); // Subtext: begins with searched word and ends at org text end
$subTextLength = strlen($subText);
if ($subTextLength > $desiredExcerptLength) { // Subtext longer than desired excerpt => cut it but keep last word
$spaceAfterLastWordPos = strpos($subText, ' ', $desiredExcerptLength);
$excerpt = substr($subText, 0, $spaceAfterLastWordPos ? $spaceAfterLastWordPos : $subTextLength);
}
else { // Subtext equal or shorter than desired excerpt => keep all text
$excerpt = $subText;
}
}
var_dump($excerpt);
It's clear way to do it.
I hope that's a behavior what you meant.
You can check it at: http://writecodeonline.com/php
There are several "kinds" of text you can pass into that:
Text where searched word isn't present=> return NULL:
input: NULL, "", "Something without searched word"=> result: NULL
Text with spaces longer than desired excerpt length (searched word length + e.g. 35)=> return org text cut out but keep whole last word:
"This text is longer and will be definitely cut but last word survives..." => "This text is longer and will be definitely"
Text with spaces equal to desired excerpt length=> return org text:
"This text is exactly the same length..." => "This text is exactly the same length..."
Text with spaces shorter than desired excerpt length=> return org text:
"This text is shorter..." => "This text is shorter..."
Text without spaces longer than desired excerpt length=> return org text:
"This_text_has_no_spaces_and_is_really_longer" => "This_text_has_no_spaces_and_is_really_longer"
Text without spaces equal to desired excerpt length=> return org text:
"This_text_has_no_spaces_and_is_the_same" => "This_text_has_no_spaces_and_is_the_same"
Text without spaces shorter than desired excerpt length=> return org text:
"This_text_has_no_spaces_and_is_shorter" => "This_text_has_no_spaces_and_is_shorter"

Related

php preg_match excluding text within html tags/attributes to find correct place to cut a string

I am trying to determine the absolute position of certain words within a block of html, but only if they are outside of an actual html tag. For instance, if I wanted to determine the position of the word "join" using preg_match in this text:
<p>There are 14 more days until our holiday special so come join us!</p>
I could use:
preg_match('/join/', $post_content, $matches, PREG_OFFSET_CAPTURE, $offset);
The problem is that this is matching the word within the aria-label attribute, when what I need is the one just after the link. It would be fine to match between the <a> and </a>, just not inside the brackets themselves.
My actual end goal, most of what (I think) I have aside from this last element: I am trimming a block of html (not a full document) to cut off at a specific word count. I am trying to determine which character that last word ends at, and then joining the left side of the html block with only the html from the right side, so all html tags close gracefully. I thought I had it working until I ran into an example like I showed where the last word was also within an html attribute, causing me to split the string at the wrong location. This is my code so far:
$post_content = strip_tags ( $p->post_content, "<a><br><p><ul><li>" );
$post_content_stripped = strip_tags ( $p->post_content );
$post_content_stripped = preg_replace("/[^A-Za-z0-9 ]/", ' ', $post_content_stripped);
$post_content_stripped = preg_replace("/\s+/", ' ', $post_content_stripped);
$post_content_stripped_array = explode ( " " , trim($post_content_stripped) );
$excerpt_wordcount = count( $post_content_stripped_array );
$cutpos = 0;
while($excerpt_wordcount>48){
$thiswordrev = "/" . strrev($post_content_stripped_array[$excerpt_wordcount - 1]) . "/";
preg_match($thiswordrev, strrev($post_content), $matches, PREG_OFFSET_CAPTURE, $cutpos);
$cutpos = $matches[0][1] + (strlen($thiswordrev) - 2);
array_pop($post_content_stripped_array);
$excerpt_wordcount = count( $post_content_stripped_array );
}
if($pwordcount>$excerpt_wordcount){
preg_match_all('/<\/?[^>]*>/', substr( $post_content, strlen($post_content) - $cutpos ), $closetags_result);
$excerpt_closetags = "" . $closetags_result[0][0];
$post_excerpt = substr( $post_content, 0, strlen($post_content) - $cutpos ) . $excerpt_closetags;
}else{
$post_excerpt = $post_content;
}
I am actually searching the string in reverse in this case, since I am walking word by word backwards from the end of the string, so I know that my html brackets are backwards, eg:
>p/<!su nioj emoc os >a/<laiceps yadiloh>"su nioj"=lebal-aira "renepoon rerreferon"=ler "knalb_"=tegrat "lmth.egapemos/"=ferh a< ruo litnu syad erom 41 era erehT>p<
But it's easy enough to flip all of the brackets before doing the preg_match, or I am assuming should be easy enough to have the preg_match account for that.
Do not use regex to parse HTML.
You have a simple objective: limit the text content to a given number of words, ensuring that the HTML remains valid.
To this end, I would suggest looping through text nodes until you count a certain number of words, and then removing everything after that.
$dom = new DOMDocument();
$dom->loadHTML($post_content);
$xpath = new DOMXPath($dom);
$all_text_nodes = $xpath->query("//text()");
$words_left = 48;
foreach( $all_text_nodes as $text_node) {
$text = $text_node->textContent;
$words = explode(" ", $text); // TODO: maybe preg_split on /\s/ to support more whitespace types
$word_count = count($words);
if( $word_count < $words_left) {
$words_left -= $word_count;
continue;
}
// reached the threshold
$words_that_fit = implode(" ", array_slice($words, 0, $words_left));
// If the above TODO is implemented, this will need to be adjusted to keep the specific whitespace characters
$text_node->textContent = $words_that_fit;
$remove_after = $text_node;
while( $remove_after->parentNode) {
while( $remove_after->nextSibling) {
$remove_after->parentNode->removeChild($remove_after->nextSibling);
}
$remove_after = $remove_after->parentNode;
}
break;
}
$output = substr($dom->saveHTML($dom->getElementsByTagName("body")->item(0)), strlen("<body>"), -strlen("</body>"));
Live demo
Ok, I figured out a workaround. I don't know if this is the most elegant solution, so if someone sees a better one I would still love to hear it, but for now I realized that I don't have to actually have the html in the string I am searching to determine the position to cut, I just need it to be the same length. I grabbed all of the html elements and just created a dummy string replacing all of them with the same number of asterisks:
// create faux string with placeholders instead of html for search purposes
preg_match_all('/<\/?[^>]*>/', $post_content, $alltags_result);
$tagcount = count( $alltags_result );
$post_content_dummy = $post_content;
foreach($alltags_result[0] as $thistag){
$post_content_dummy = str_replace($thistag, str_repeat("*",strlen($thistag)), $post_content_dummy);
}
Then I just use $post_content_dummy in the while loop instead of $post_content, in order to find the cut position, and then $post_content for the actual cut. So far seems to be working fine.

How to find, make link and shorten url text in text block with PHP

So I currently have this...
<?php
$textblockwithformatedlinkstoecho = preg_replace('!(((f|ht)tp(s)?://)[-a-zA-
Zа-яА-Я()0-9#:%_+.~#?&;//=]+)!i', '$1',
$origtextwithlinks);
echo $textblockwithformatedlinkstoecho;
?>
But, I would like to also shorten the clickable link to around 15 chars in length...
Example input text
I recommend you visit http://www.example.com/folder1/folder2/page3.html?
longtext=ugsdfhsglshghsdghlsg8ysd87t8sdts8dtsdtygs9ysd908yfsd0fyu for more
information.
Required output text
I recommend you visit example.com/fol... for more information.
You can use preg_replace_callback() to manipulate the matches.
Example:
$text = "I recommend you visit http://www.example.com/folder1/folder2/page3.html?longtext=ugsdfhsglshghsdghlsg8ys\d87t8sdts8\dtsdtygs9ysd908yfsd0fyu for more information.";
$fixed = preg_replace_callback(
'!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9#:%_+.~#?&;//=]+)!i',
function($matches) {
// Get the fully matched url
$url = $matches[0];
// Do some magic for the link text, like only show the first 15 characters
$text = strlen($url) > 15
? substr($url, 0, 15) . '...'
: $url;
// Return the new html link
return '' . $text . '';
},
$text
);
echo $fixed;
You probably need to modify your regex though, since it doesn't match the \-characters you have in the query string in the url.

Modify a php limit text function adding some kind of offset to it

Maybe you guys can help:
I have a variable called $bio with bio data.
$bio = "Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way cooler then the author of the question";
I search the $bio using a set of functions to search for a certain word, lets say "author" which adds a span class around that word, and I get:
$bio = "Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way cooler then the <span class=\"highlight\">author</span> of the question";
I use a function to limit the text to 85 chars:
$bio = limit_text($bio,85);
The problem is when there are more then 80 chars before the word "author" in $bio.
When the limit_text() is applied, I won't see the highlighted word author.
What I need is for the limit_text() function to work as normal, adding all the words that contain the span class highlight at the end.
Something like this:
*"This is the limited text to 85 chars, but there are no words with the span class highlight so I am putting to be continued ... **author**, **author2** (and all the other words that have a span class highlight around them separate by comma "*
Hope you understood what I mean, if not, please comment and I'll try to explain better.
Here is my limit_text() function:
function limit_text($text, $length){ // Limit Text
if(strlen($text) > $length) {
$stringCut = substr($text, 0, $length);
$text = substr($stringCut, 0, strrpos($stringCut, ' '));
}
return $text;
}
UPDATE:
$xturnons = str_replace(",", ", ", $xturnons);
$xbio = str_replace(",", ", ", $xbio);
$xbio = customHighlights($xbio,$toHighlight);
$xturnons = customHighlights($xturnons,$toHighlight);
$xbio = limit_text($xbio,85);
$xturnons = limit_text($xturnons,85);
The customHighlights function which adds the span class highlighted:
function addRegEx($word){ // Highlight Words
return "/" . $word . '[^ ,\,,.,?,\.]*/i';
}
function highlight($word){
return "<span class='highlighted'>".$word[0]."</span>";
}
function customHighlights($searchString,$toHighlight){
$searchFor = array_map('addRegEx',$toHighlight);
$result = preg_replace_callback($searchFor,'highlight',$searchString);
return $result;
}
This change to your limit_text function will take the text, and cut it if it's longer than the given $length. If you pass a $needle to it, it will search for the first occurrence of it, and end your sentence with it.
Also, if the text is cut before it's actual length, it will add $addition to it, while still preserving the limit of $length characters.
I've included a usage and a sample output based on your given below:
<?php
/**
* $text - The text to cut from
* $length - The amount of characters that should be returned
* $needle - If needle is given and found in the text, and it is
* at least $length far from the start of the string - it will end the sentence with it.
* $addition - If the sentence was cut in the middle, will add it to the end of it.
**/
function limit_text($text, $length, $needle="", $addition="...") {
if(strlen($text) > $length) {
$length -= strlen($addition);
$start = 0;
$trimLast = true;
if (!empty($needle)) {
$needleStart = strpos($text, $needle);
if ($needleStart > $length) {
$length -= strlen($needle);
$start = $needleStart + strlen($needle) - $length;
$trimLast = false;
}
}
$stringCut = substr($text, max(0, $start), $length);
if ($start > 0) {
$stringCut = substr($stringCut, strpos($stringCut, ' ')+1);
}
if ($trimLast) {
$lastWhitespace = strrpos($stringCut, ' ');
$stringCut = substr($stringCut, 0, $lastWhitespace);
}
// split into words (so we won't replace words that contain it in the middle)
// and wrap $needle with <span class="highlighted"></span>
if (!empty($needle)) {
$words = explode(" ", $stringCut);
$needles = array_keys($words, $needle);
foreach ($needles as $needleKey) {
$words[$needleKey] = "<span class=\"highlighted\">$needle</span>";
}
$stringCut = implode(" ", $words);
}
$text = $stringCut.$addition;
}
return $text;
}
$bio = "Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way cooler then the author of the question";
$text = limit_text($bio, 85, "author");
var_dump($text);
Output:
string (111) "fast cars and boats. I work as a blogger and I'm way cooler then the <span class="highlighted">author</span>..."
First, you need to make sure you don't break words apart by shortening the string. Then you need to append all of the <span class="highlight"> tokens to the end of the shortened string. Here is what I came up with (in about 8 lines!):
function limit_text($text, $length){
if( strlen( $text) < $length) {
return $text;
}
// Truncate the string without breaking words
list( $wrapped) = explode("\n", wordwrap( $text, $length));
// Get the span of text occurring after the wrapped string
$remainder = substr( $text, strlen( $wrapped));
// Add the "to be continued" to $wrapped
$wrapped .= ' to be continued ... ';
// Now, grab all of the <span class="highlight"></span> tags in the $remainder
preg_match_all( '#<span class="highlight">[^<]+</span>#i', $remainder, $matches);
// Add the <span> tags to the end of the string, separated by a comma, if present
$wrapped .= implode( ', ', $matches[0]);
return $wrapped;
}
Now, with your original test:
$bio = "Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way cooler then the <span class=\"highlight\">author</span> of the question";
$bio = limit_text( $bio,85);
var_dump( htmlentities( $bio));
This outputs:
string(165) "Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way to be continued ... <span class="highlight">author</span>"
Now, another test with multiple <span> tags:
This outputs:
$bio = 'Hello, what about a <span class="highlight">span tag</span> before the limit? Or what if I have many <span class="highlight">span tags</span> <span class="highlight">after</span> <span class="highlight">the</span> limit?';
$bio = limit_text( $bio,85);
var_dump( htmlentities( $bio));
string(308) "Hello, what about a <span class="highlight">span tag</span> before the limit? Or what to be continued ... <span class="highlight">span tags</span>, <span class="highlight">after</span>, <span class="highlight">the</span>"
If you have more test cases, or have a modification to the function above, let me know and I can fix it!
Judging from your requirements, this should do what you want:
function get_highlighted_string($s)
{
return '<span class="highlight">' . htmlspecialchars($s) . '</span>';
}
function limit_text($text, $max_length, array $keywords = array(), $continued = '...')
{
// highlights to put after the cut string
$extra = array();
// highlight keywords
if ($keywords) {
$re = '~\b(' . join('|', array_map('preg_quote', $keywords, array('~'))) . ')\b~i';
// get all matches and capture their positions as well
if (preg_match_all($re, $text, $matches, PREG_OFFSET_CAPTURE)) {
// we reverse the matches by position to make replacement easier
foreach (array_reverse($matches[1]) as $match) {
// $match[0] = match
// $match[1] = start position
$match_len = strlen($match[0]);
if ($match[1] + $match_len <= $max_length) {
// still fits in cut string
$match_replacement = get_highlighted_string($match[0]);
$text = substr_replace($text, $match_replacement, $match[1], $match_len);
// update max length
$max_length = $max_length - $match_len + strlen($match_replacement);
} else {
// will not fit in the cut string, so we place it outside
array_unshift($extra, get_highlighted_string($match[0]));
}
}
}
// use wordwrap and strcspn to cut the string by word boundaries
if (strlen($text) > $max_length) {
$text = substr($text, 0, strcspn(wordwrap($text, $max_length, "\0"), "\0")) . " $continued";
}
}
if ($extra) {
// append what we couldn't fit in the cut string
$text .= ' ' . join(', ', $extra);
}
return $text;
}
Example:
echo limit_text("Hello, I like fast cars and boats. I work as a blogger I'm way cooler then the author of the question", 85, array('author', 'question'));
Hello, I like fast cars and boats. I work as a blogger I'm way cooler then the <span class="highlight">author</span> ... <span class="highlight">question</span>
In the example, the cut-off is exactly at author so that highlight comes before the ... while the question keywords gets put behind.
Another example:
echo limit_text("Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way cooler then the author of the question", 85, array('author', 'question'));
Hello, I am John, I'm 25, I like fast cars and boats. I work as a blogger and I'm way ... <span class="highlight">author</span>, <span class="highlight">question</span>
Both keywords are beyond the 85 character marker, so they are appended at the back, comma separated.
Let me know if this works for you :)

Replace text ignoring HTML tags

I have a simple text with HTML tags, for example:
Once <u>the</u> activity reaches the resumed state, you can freely add and remove fragments to the activity. Thus, <i>only</i> while the activity is in the resumed state can the <b>lifecycle</b> of a <hr/> fragment change independently.
I need to replace some parts of this text ignoring its html tags when I do this replace, for example this string - Thus, <i>only</i> while I need to replace with my string Hello, <i>its only</i> while . Text and strings to be replaced are dynamically. I need your help with my preg_replace pattern
$text = '<b>Some html</b> tags with <u>and</u> there are a lot of tags <i>in</i> this text';
$arrayKeys= array('Some html' => 'My html', 'and there' => 'is there', 'in this text' => 'in this code');
foreach ($arrayKeys as $key => $value)
$text = preg_replace('...$key...', '...$value...', $text);
echo $text; // output should be: <b>My html</b> tags with <u>is</u> there are a lot of tags <i>in</i> this code';
Please help me to find solution. Thank you
Basically we're going to build dynamic arrays of matches and patterns off of plain text using Regex. This code only matches what was originally asked for, but you should be able to get an idea of how to edit the code from the way I've spelled it all out. We're catching either an open or a close tag and white space as a passthru variable and replacing the text around it. This is setup based on two and three word combinations.
<?php
$text = '<b>Some html</b> tags with <u>and</u> there are a lot of tags <i>in</i> this text';
$arrayKeys= array(
'Some html' => 'My html',
'and there' => 'is there',
'in this text' =>'in this code');
function make_pattern($string){
$patterns = array(
'!(\w+)!i',
'#^#',
'! !',
'#$#');
$replacements = array(
"($1)",
'!',
//This next line is where we capture the possible tag or
//whitespace so we can ignore it and pass it through.
'(\s?<?/?[^>]*>?\s?)',
'!i');
$new_string = preg_replace($patterns,$replacements,$string);
return $new_string;
}
function make_replacement($replacement){
$patterns = array(
'!^(\w+)(\s+)(\w+)(\s+)(\w+)$!',
'!^(\w+)(\s+)(\w+)$!');
$replacements = array(
'$1\$2$3\$4$5',
'$1\$2$3');
$new_replacement = preg_replace($patterns,$replacements,$replacement);
return $new_replacement;
}
foreach ($arrayKeys as $key => $value){
$new_Patterns[] = make_pattern($key);
$new_Replacements[] = make_replacement($value);
}
//For debugging
//print_r($new_Patterns);
//print_r($new_Replacements);
$new_text = preg_replace($new_Patterns,$new_Replacements,$text);
echo $new_text."\n";
echo $text;
?>
Output
<b>My html</b> tags with <u>is</u> there are a lot of tags <i>in</i> this code
<b>Some html</b> tags with <u>and</u> there are a lot of tags <i>in</i> this text
Here we go. this piece of code should work, assuming you're respecting only twp constraints :
Pattern and replacement must have the same number of words. (Logical, since you want to keep position)
You must not split a word around a tag. (<b>Hel</b>lo World won't work.)
But if these are respected, this should work just fine !
<?php
// Splits a string in parts delimited with the sequence.
// '<b>Hey</b> you' becomes '~-=<b>~-=Hey~-=</b>~-= you' that make us get
// array ("<b>", "Hey" " you")
function getTextArray ($text, $special) {
$text = preg_replace ('#(<.*>)#isU', $special . '$1' . $special, $text); // Adding spaces to make explode work fine.
return preg_split ('#' . $special . '#', $text, -1, PREG_SPLIT_NO_EMPTY);
}
$text = "
<html>
<div>
<p>
<b>Hey</b> you ! No, you don't have <em>to</em> go!
</p>
</div>
</html>";
$replacement = array (
"Hey you" => "Bye me",
"have to" => "need to",
"to go" => "to run");
// This is a special sequence that you must be sure to find nowhere in your code. It is used to split sequences, and will disappear.
$special = '~-=';
$text_array = getTextArray ($text, $special);
// $restore is the array that will finally contain the result.
// Now we're only storing the tags.
// We'll be story the text later.
//
// $clean_text is the text without the tags, but with the special sequence instead.
$restore = array ();
for ($i = 0; $i < sizeof ($text_array); $i++) {
$str = $text_array[$i];
if (preg_match('#<.+>#', $str)) {
$restore[$i] = $str;
$clean_text .= $special;
}
else {
$clean_text .= $str;
}
}
// Here comes the tricky part.
// We wanna keep the position of each part of the text so the tags don't
// move after.
// So we're making the regex look like (~-=)*Hey(~-=)* you(~-=)*
// And the replacement look like $1Bye$2 me $3.
// So that we keep the separators at the right place.
foreach ($replacement as $regex => $newstr) {
$regex_array = explode (' ', $regex);
$regex = '(' . $special . '*)' . implode ('(' . $special . '*) ', $regex_array) . '(' . $special . '*)';
$newstr_array = explode (' ', $newstr);
$newstr = "$1";
for ($i = 0; $i < count ($regex_array) - 1; $i++) {
$newstr .= $newstr_array[$i] . '$' . ($i + 2) . ' ';
}
$newstr .= $newstr_array[count($regex_array) - 1] . '$' . (count ($regex_array) + 1);
$clean_text = preg_replace ('#' . $regex . '#isU', $newstr, $clean_text);
}
// Here we re-split one last time.
$clean_text_array = preg_split ('#' . $special . '#', $clean_text, -1, PREG_SPLIT_NO_EMPTY);
// And we merge with $restore.
for ($i = 0, $j = 0; $i < count ($text_array); $i++) {
if (!isset($restore[$i])) {
$restore[$i] = $clean_text_array[$j];
$j++;
}
}
// Now we reorder everything, and make it go back to a string.
ksort ($restore);
$result = implode ($restore);
echo $result;
?>
Will output Bye me ! No, you don't need to run!
[EDIT] Now supporting a custom pattern, which allows to avoid adding useless spaces.

Display post excerpts, limited by word count

I am working on my php website (Not a Wordpress site) on the main index I display the two newest post. The thing is on the description it shows the entire article I find myself needing to display post excerpts maybe 35 word limit.
<?=$line["m_description"]?>
<?
$qresult3 = mysql_query("SELECT * FROM t_users WHERE u_id=".$line["m_userid"]." LIMIT 1");
if (mysql_num_rows($qresult3)<1) { ?>
<?php
// just the excerpt
function first_n_words($text, $number_of_words) {
// Where excerpts are concerned, HTML tends to behave
// like the proverbial ogre in the china shop, so best to strip that
$text = strip_tags($text);
// \w[\w'-]* allows for any word character (a-zA-Z0-9_) and also contractions
// and hyphenated words like 'range-finder' or "it's"
// the /s flags means that . matches \n, so this can match multiple lines
$text = preg_replace("/^\W*((\w[\w'-]*\b\W*){1,$number_of_words}).*/ms", '\\1', $text);
// strip out newline characters from our excerpt
return str_replace("\n", "", $text);
}
// excerpt plus link if shortened
function truncate_to_n_words($text, $number_of_words, $url, $readmore = 'read more') {
$text = strip_tags($text);
$excerpt = first_n_words($text, $number_of_words);
// we can't just look at the length or try == because we strip carriage returns
if( str_word_count($text) !== str_word_count($excerpt) ) {
$excerpt .= '... '.$readmore.'';
}
return $excerpt;
}
$src = <<<EOF
<b>My cool story</b>
<p>Here it is. It's really cool. I like it. I like lots of stuff.</p>
<p>I also like to read and write and carry on forever</p>
EOF;
echo first_n_words($src, 10);
echo "\n\n-----------------------------\n\n";
echo truncate_to_n_words($src, 10, 'http://www.google.com');
EDIT: Added functional example and accounted for punctuation and numbers in text
I have a function though other people may say it's not good because I'm still good at PHP too (tips welcome people) but this will give you what you are looking for, it may need better coding if anyone has suggestions.
function Short($text, $length, $url, $more){
$short = mb_substr($text, 0, $length);
if($short != $text) {
$lastspace = strrpos($short, ' ');
$short = substr($short , 0, $lastspace);
if(!$more){
$more = "Read Full Post";
} // end if more is blank
$short .= "...[<a href='$url'>$more</a>]";
} // end if content != short
$short = str_replace("’","'", $short);
$short = stripslashes($short);
$short = nl2br($short);
} // end short function
To Use:
say your article content is the variable $content
function($content, "35", "http://domain.com/article_post", "Read Full Story");
echo $short;
Similarly, you can adjust the function to remove $url and $more from it and just have the excerpt with ... at the end.

Categories