preg_replace inside loop replacing last match instead of each - php

Sorry about the title I honestly dont know how to explain it properly.
I am making a small shortcode function that needs to replace shortcodes with html output.
The preg_match_all finds everything I need but the preg_replace is replacing the same match over and over again.
Here is the demo
https://eval.in/139727
I am sure I made a mess in those foreach loops but just cant figure it out.
$text = 'Some text and some [link link="linkhref1" text="Text1"],[link link="linkhref2" text="Text2"]';
function shortcodes($text) {
$shortcodes = array(
'link' => array(
"check" => "[link",
"type" => "link",
"match" => "#\[link(.*?)link\=\"(.*?)\"(.*?)text\=\"(.*?)\"#Ui",
"replace" => "/\[link(.*?)\]/s"
)
);
foreach ($shortcodes as $index => $shortcode) {
if (strpos($text, $shortcode['check']) !== false) {
$text = shortcode_replace($shortcode, $text);
}
}
return $text;
}
function shortcode_replace($shortcode, $text) {
$replacement = '';
preg_match_all($shortcode['match'], $text, $matches);
switch ($shortcode['type']) {
case "link":
foreach ($matches[4] as $index => $match) {
$link = $matches[2][$index];
$linktext = $matches[4][$index];
$replacement .= '' . $linktext . '';
$text = preg_replace($shortcode['replace'], $replacement, $text);
}
}
return $text;
}
echo shortcodes($text);
any help is appreciated!

There was a problem in regex i've changed it. Also you don't need preg_replace there.
<?php
$text = 'Some text and some [link link="linkhref1" text="Text1"],[link link="linkhref2" text="Text2"]';
function shortcodes($text) {
$shortcodes = array(
'link' => array(
"check" => "[link",
"type" => "link",
"match" => "#\[link(\s+)link\=\"([^\"]+)\"(\s+)text\=\"([^\"]+)\"\]#Ui",
"replace" => "/\[link(.*?)\]/s"
)
);
foreach ($shortcodes as $index => $shortcode) {
if (strpos($text, $shortcode['check']) !== false) {
$text = shortcode_replace($shortcode, $text);
}
}
return $text;
}
function shortcode_replace($shortcode, $text) {
$replace = '';
preg_match_all($shortcode['match'], $text, $matches);
switch ($shortcode['type']) {
case "link":
var_dump($matches);
foreach ($matches[4] as $index => $match) {
$link = $matches[2][$index];
$linktext = $matches[4][$index];
$replace = '' . $linktext . '';
$text = str_replace($matches[0][$index], $replace, $text);
}
}
return $text;
}
echo shortcodes($text);

Here is a working version :
<?php
$text = 'Some text and some [link link="linkhref1" text="Text1"],[link link="linkhref2" text="Text2"]';
function shortcodes($text) {
$shortcodes = array(
'link' => array(
"check" => "[link",
"type" => "link",
"match" => "#\[link(.*?)link\=\"(.*?)\"(.*?)text\=\"(.*?)\"#",
"replace" => "/\[link(.*?)\]/s"
)
);
foreach ($shortcodes as $index => $shortcode) {
if (strpos($text, $shortcode['check']) !== false) {
$text = shortcode_replace($shortcode, $text);
}
}
return $text;
}
function shortcode_replace($shortcode, $text) {
$replace = '';
preg_match_all($shortcode['match'], $text, $matches);
switch ($shortcode['type']) {
case "link":
foreach ($matches[4] as $index => $match) {
$link = $matches[2][$index];
$linktext = $matches[4][$index];
$replace .= '' . $linktext . '';
$whatToReplace = '[link link="'.$link.'" text="'.$linktext.'"]';
$text = str_replace($whatToReplace, $replace, $text);
}
}
return $text;
}
echo shortcodes($text);
I'm not very good at RegExp, i modified the "match" to match all the links ( with what you got, it didn't )
You need to identify the exact [link ] to replace, and not all all of them. In my opinion, this is the correct way to identify the link, You can also identify it using strpos() ( getting the start and end of the string) or see where the [link starts and the first ] is .
A better option can be to create a regexp with the unique values for it , which can compensate for extra spaces between tags
Hopefully this is of help to you

Related

php shorten all urls with is.gd api in a string and linkify them

As the title says I'm trying to shorten all urls with is.gd api in a string and linkify them.
function link_isgd($text)
{
$regex = '#(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-~]*(\?\S+)?)?)?)#';
preg_match_all($regex, $text, $matches);
foreach($matches[0] as $longurl)
{
$tiny = file_get_contents('http://isnot.gd/api.php?longurl='.$longurl.'&format=json');
$json = json_decode($tiny, true);
foreach($json as $key => $value)
{
if ($key == 'errorcode')
{
$link = $longurl;
}
else if ($key == 'shorturl')
{
$link = $value;
}
}
}
return preg_replace($regex, ''.$link.'', $text);
}
$txt = 'Some text with links https://www.abcdefg.com/123 blah blah blah https://nooodle.com';
echo link_isgd($txt);
This is what I have got so far, linkifying works and so does shortening if only 1 url is in the string, however if theres 2 or more they all end up the same.
Note: the is.gd was not allowed in post, SO thought I was posting a short link which is not allowed here, so I had to change it to isnot.gd.
Your variable $link is not array so it takes only last assigned value of $link. You can replace preg_replace with str_replace and pass arrays with matches and links.
You can also use preg_replace_callback() and you can pass $matches directly to function which will replace with link. https://stackoverflow.com/a/9416265/7082164
function link_isgd($text)
{
$regex = '#(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-~]*(\?\S+)?)?)?)#';
preg_match_all($regex, $text, $matches);
$links = [];
foreach ($matches[0] as $longurl) {
$tiny = file_get_contents('http://isnot.gd/api.php?longurl=' . $longurl . '&format=json');
$json = json_decode($tiny, true);
foreach ($json as $key => $value) {
if ($key == 'errorcode') {
$links[] = $longurl;
} else if ($key == 'shorturl') {
$links[] = $value;
}
}
}
$links = array_map(function ($el) {
return '' . $el . '';
}, $links);
return str_replace($matches[0], $links, $text);
}

find and replace country from string

I have an array of countries the key is the country code, the value is the country name, now i have a string, which is posted by users, i want to find if the string has country in it n replace it with
<span class="country">$1</span>
to make it even clearer : let's say i have this text :
Canada is a cold place
i want it to be :
<span class="country">canada</span> is a cold place
where i use my countries array to find and repalce.
the reason behind this is i want to use the microformats, so i need to extract specific text from a string.
i had similar preg_replaces code
$style = array(
'/\[b\](.*)?\[\/b\]/isU' => '<b>$1</b>',
'/\[i\](.*)?\[\/i\]/isU' => '<i>$1</i>',
'/\[u\](.*)?\[\/u\]/isU' => '<u>$1</u>',
'/\[em\](.*)?\[\/em\]/isU' => '<em>$1</em>',
'/\[li\](.*)?\[\/li\]/isU' => '<li>$1</li>',
'/\[code\](.*)?\[\/code\]/isU' => '<div class="tx_code">$1</div>',
'/\[q\](.*)?\[\/q\]/isU' => '<q>$1</q>',
'/[\r\n]{3}+/' => "\n"
);
$text = preg_replace(array_keys($style),array_values($style),$text);
which works, i need something like that.
Keep in mind, that it should not be case sensitive, some users may post canada or Canada
thanks
try this
function findword($text,array $List){
foreach($List as $Val)
$pattern['%([^\da-zA-Z]+)'.$Val.'([^\da-zA-Z]+)%si'] = '<span class="country">'.$Val.'</span>';
$text = preg_replace(array_keys($pattern), array_values($pattern), ' '.$text.' ');
return $text;
}
echo findword('Canada is a cold place',array('Canada'));
output:
<span class="country">Canada</span>is a cold place
Edit: if you want replace all match word in text you can use this
function findword($text,array $List){
foreach($List as $Val)
$pattern['~'.$Val.'~si'] = '<span class="country">'.$Val.'</span>';
$text = preg_replace(array_keys($pattern), array_values($pattern), ' '.$text.' ');
return $text;
}
echo findword('Canadaisacold place',array('Canada'));
output:
<span class="country">Canada</span>isacold place
Edit2: i wrote it by DOMDocument That Work Good in Html
class XmlRead{
static function Clean($html){
$html=preg_replace_callback("~<script(.*?)>(.*?)</script>~si",function($m){
//print_r($m);
// $m[2]=preg_replace("/\/\*(.*?)\*\/|[\t\r\n]/s"," ", " ".$m[2]." ");
$m[2]=preg_replace("~//(.*?)\n~si"," ", " ".$m[2]." ");
//echo $m[2];
return "<script ".$m[1].">".$m[2]."</script>";
}, $html);
$search = array(
"/\/\*(.*?)\*\/|[\t\r\n]/s" => "",
"/ +\{ +|\{ +| +\{/" => "{",
"/ +\} +|\} +| +\}/" => "}",
"/ +: +|: +| +:/" => ":",
"/ +; +|; +| +;/" => ";",
"/ +, +|, +| +,/" => ","
);
$html = preg_replace(array_keys($search), array_values($search), $html);
preg_match_all('!(<(?:code|pre|script).*>[^<]+</(?:code|pre|script)>)!',$html,$pre);
$html = preg_replace('!<(?:code|pre).*>[^<]+</(?:code|pre)>!', '#pre#', $html);
$html = preg_replace('#<!–[^\[].+–>#', '', $html);
$html = preg_replace('/[\r\n\t]+/', ' ', $html);
$html = preg_replace('/>[\s]+</', '><', $html);
$html = preg_replace('/\s+/', ' ', $html);
if (!empty($pre[0])) {
foreach ($pre[0] as $tag) {
$html = preg_replace('!#pre#!', $tag, $html,1);
}
}
return($html);
}
function loadNprepare($content,$encod='') {
$content=self::Clean($content);
//$content=html_entity_decode(html_entity_decode($content));
// $content=htmlspecialchars_decode($content,ENT_HTML5);
$DataPage='';
if(preg_match('~<body(.*?)>(.*?)</body>~si',$content,$M)){
$DataPage=$M[2];
}else{
$DataPage =$content;
}
$HTML=$DataPage;
$HTML="<!doctype html><html><head><meta charset=\"utf-8\"><title>Untitled Document</title></head><body>".$HTML."</body></html>";
$dom= new DOMDocument;
$HTML = str_replace("&", "&", $HTML); // disguise &s going IN to loadXML()
// $dom->substituteEntities = true; // collapse &s going OUT to transformToXML()
$dom->recover = TRUE;
#$dom->loadHTML('<?xml encoding="UTF-8">' .$HTML);
// dirty fix
foreach ($dom->childNodes as $item)
if ($item->nodeType == XML_PI_NODE)
$dom->removeChild($item); // remove hack
$dom->encoding = 'UTF-8'; // insert proper
return $dom;
}
function GetBYClass($Doc,$ClassName){
$finder = new DomXPath($Doc);
return($finder->query("//*[contains(#class, '$ClassName')]"));
}
function findword($text,array $List){
foreach($List as $Val)
$pattern['%(\#)?([^\da-zA-Z]+)'.$Val.'([^\da-zA-Z]+)%si'] = '<span class="country">'.$Val.'</span>';
$text = preg_replace(array_keys($pattern), array_values($pattern), ' '.$text.' ');
return $text;
}
function FindAndReplace($node,array $List) {
if($node==NULL)return false;
if (XML_TEXT_NODE === $node->nodeType || XML_CDATA_SECTION_NODE === $node->nodeType) {
$node->nodeValue=$this->findword($node->nodeValue,$List);
return;
}else{
if(is_object($node->childNodes) or is_array($node->childNodes)) {
foreach($node->childNodes as $childNode) {
$this->FindAndReplace($childNode,$List);
}
}
}
}
function DOMinnerHTML($element)
{
$innerHTML = "";
$children = $element->childNodes;
foreach ($children as $child)
{
$tmp_dom = new DOMDocument();
$tmp_dom->appendChild($tmp_dom->importNode($child, true));
$innerHTML.=trim($tmp_dom->saveHTML());
}
$innerHTML=html_entity_decode(html_entity_decode($innerHTML));
return $innerHTML;
}
function DOMRemove(DOMNode $from) {
$from->parentNode->removeChild($from);
}
}
$XmlRead=new XmlRead();
$Doc=$XmlRead->loadNprepare('Canada is a cold place');
$XmlRead->FindAndReplace($Doc,array('Canada'));
$Body=$Doc->getElementsByTagName('body')->item(0);
echo $XmlRead->DOMinnerHTML($Body);
output
<span class="country">Canada</span>is a cold place
i wrote my own, and it was the best so far :
if($microformat){
foreach ($this->countries as $co){
$text = preg_replace('/(\#)?\b'.$co.'\b/isU','<span class="country">$0</span>',$text);
}
}
thank you all

PHP - replace text with smiley

I have this function:
function bb_parse($string) {
$string = $this->quote($string);
$string=nl2br($string);
$string = html_entity_decode(stripslashes(stripslashes($string)));
$tags = 'b|i|u';
while (preg_match_all('`\[('.$tags.')=?(.*?)\](.+?)\[/\1\]`', $string, $matches)) foreach ($matches[0] as $key => $match) {
list($tag, $param, $innertext) = array($matches[1][$key], $matches[2][$key], $matches[3][$key]);
switch ($tag) {
case 'b': $replacement = "<strong>$innertext</strong>"; break;
case 'i': $replacement = "<em>$innertext</em>"; break;
case 'u': $replacement = "<u>$innertext</u>"; break;
}
$string = str_replace($match, $replacement, $string);
}
return $string;
}
As you can see, I can easily make BBCode with bold, italic and underline. Although, I am trying to add smileys to this function as well, but without luck.
I tried to simply just add :) to the $tags, and then add the smiley :) img in the case, but that did not work.
How can I do, so I can also add smilies to this?
Thanks in advance.
Just create a function that does a simple str_replace, I'd say:
<?php
function smilies( $text ) {
$smilies = array(
';)' => '<img src="wink.png" />',
':)' => '<img src="smile.png" />'
);
return str_replace( array_keys( $smilies ), array_values( $smilies ), $text );
}
$string = '[b]hello[/b] smile: :)';
echo smilies( bb_parse( $string ) );
http://php.net/manual/en/function.preg-quote.php
You have to escape your smiley tags.
$tags = 'b|i|u|'.preg_quote(":)");

replace everything except specific words in PHP

Is it possible to use php's preg_replace to remove anything in a string except specific words?
For example:
$text = 'Hello, this is a test string from php.';
I want to remove everything except "test" and "php" so it will be:
$text will be 'test php'
You could always use a callback. Under PHP 5.3:
$keep = array('test'=>1, 'php'=>1);
$text = trim(
preg_replace(
'/[^A-Za-z]+/', ' ',
preg_replace_callback(
'/[A-Za-z]+/',
function ($matched) use (&keep) {
if (isset($keep[$matched[0]])) {
return $matched[0];
}
return '';
}, $text
) ) );
Alternatively:
$text =
array_intersect(
preg_split('/[^A-Za-z]+/', $text),
array('test', 'php')
);
$text = 'Hello, this is a test string from php.';
$words = preg_split('~\W~', $text, -1, PREG_SPLIT_NO_EMPTY);
$allowed_words = array('test'=>1, 'php'=>1);
$output = array();
foreach($words as $word)
{
if(isset($allowed_words[$word]))
{
$output[] = $word;
}
}
print implode(' ', $output);

How to use preg_replace() to apply hilight_string() to content between BBCode-like tags?

I'm trying to run the preg_replace() function to replace the content in between two custom tags (i.e. [xcode]) within the string / content of the page.
What I want to do with the content between these custom tags is to run it through highlight_string() function and to remove those custom tags from the output.
Any idea how to do it?
So you want sort of a BBCode parser. The example below replaces [xcode] tags with whatever markup you like.
<?php
function highlight($text) {
$text = preg_replace('#\[xcode\](.+?)\[\/xcode\]#msi', '<em>\1</em>', $text);
return $text;
}
$text = '[xcode]Lorem ipsum[/xcode] dolor sit [xcode]amet[/xcode].';
echo highlight($text);
?>
Use preg_replace_callback() if you want to pass the matched text to a function:
<?php
function parse($text) {
$text = preg_replace_callback('#\[xcode\](.+?)\[\/xcode\]#msi',
function($matches) {
return highlight_string($matches[1], 1);
}
, $text);
return $text;
}
$text = '[xcode]Lorem ipsum[/xcode] dolor sit [xcode]amet[/xcode].';
echo bbcode($text);
?>
I'll include the source code of a BBCode parser that I made a long time ago. Feel free to use it.
<?php
function bbcode_lists($text) {
$pattern = "#\[list(\=(1|a))?\](.*?)\[\/list\]#msi";
while (preg_match($pattern, $text, $matches)) {
$points = explode("[*]", $matches[3]);
array_shift($points);
for ($i = 0; $i < count($points); $i++) {
$nls = split("[\n]", $points[$i]);
$brs = count($nls) - 2;
$points[$i] = preg_replace("[\r\n]", "<br />", $points[$i], $brs);
}
$replace = ($matches[2] != '1') ? ($matches[2] != 'a') ? '<ul>' : '<ol style="list-style:lower-alpha">' : '<ol style="list-style:decimal">';
$replace .= "<li>";
$replace .= implode("</li><li>", $points);
$replace .= "</li>";
$replace .= ($matches[2] == '1' || $matches[2] == 'a' ) ? '</ol>' : '</ul>';
$text = preg_replace($pattern, $replace, $text, 1);
$text = preg_replace("[\r\n]", "", $text);
}
return $text;
}
function bbcode_parse($text) {
$text = preg_replace("[\r\n]", "<br />", $text);
$smilies = Array(
':)' => 'smile.gif',
':d' => 'tongue2.gif',
':P' => 'tongue.gif',
':lol:' => 'lol.gif',
':D' => 'biggrin.gif',
';)' => 'wink.gif',
':zzz:' => 'zzz.gif',
':confused:' => 'confused.gif'
);
foreach ($smilies as $key => $value) {
$text = str_replace($key, '<img src="/images/smilies/' . $value . '" alt="' . $key . '" />', $text);
}
if (!(!strpos($text, "[") && !strpos($text, "]"))) {
$bbcodes = Array(
'#\[b\](.*?)\[/b\]#si' => '<strong>$1</strong>',
'#\[i\](.*?)\[/i\]#si' => '<em>$1</em>',
'#\[u\](.*?)\[/u\]#si' => '<span class="u">$1</span>',
'#\[s\](.*?)\[/s\]#si' => '<span class="s">$1</span>',
'#\[size=(.*?)\](.*?)\[/size\]#si' => '<span style="font-size:$1">$2</span>',
'#\[color=(.*?)\](.*?)\[/color\]#si' => '<span style="color:$1">$2</span>',
'#\[url=(.*?)\](.*?)\[/url\]#si' => '$2',
'#\[url\](.*?)\[/url\]#si' => '$1',
'#\[img\](.*?)\[/img\]#si' => '<img src="$1" alt="" />',
'#\[code\](.*?)\[/code\]#si' => '<div class="code">$1</div>'
);
$text = preg_replace(array_keys($bbcodes), $bbcodes, $text);
$text = bbcode_lists($text);
$quote_code = Array("'\[quote=(.*?)\](.*?)'i", "'\[quote](.*?)'i", "'\[/quote\]'i");
$quote_html = Array('<blockquote><p class="quotetitle">Quote \1:</p>\2', '<blockquote>\2', '</blockquote>');
$text = preg_replace($quote_code, $quote_html, $text);
}
return $text;
}
?>
basically,
preg_replace_callback('~\[tag\](.+?)\[/tag\]~', function($matches) { whatever }, $text);
this doesn't handle nested tags though
complete example
$text = "hello [xcode] <? echo bar ?> [/xcode] world";
echo preg_replace_callback(
'~\[xcode\](.+?)\[/xcode\]~',
function($matches) {
return highlight_string($matches[1], 1);
},
$text
);
<?php
$string = 'The quick brown fox jumped over the lazy dog.';
$patterns = array();
$patterns[0] = '/quick/';
$patterns[1] = '/brown/';
$patterns[2] = '/fox/';
$replacements = array();
$replacements[2] = 'bear';
$replacements[1] = 'black';
$replacements[0] = 'slow';
echo preg_replace($patterns, $replacements, $string);
?>
The above example will output:
The bear black slow jumped over the lazy dog.
http://php.net/manual/en/function.preg-replace.php
OR
str_replace should help you
http://php.net/manual/en/function.str-replace.php
Thanks to user187291's suggestion and preg_replace_callback specification I've ended up with the following outcome which does the job spot on! :
function parseTagsRecursive($input)
{
$regex = '~\[xcode\](.+?)\[/xcode\]~';
if (is_array($input)) {
$input = highlight_string($input[1], true);
}
return preg_replace_callback($regex, 'parseTagsRecursive', $input);
}
$text = "hello [xcode] <? echo bar ?> [/xcode] world and [xcode] <?php phpinfo(); ?> [/xcode]";
echo parseTagsRecursive($text);
The output of parsing the $text variable through this function is:
hello <? echo bar ?> world and <?php phpinfo(); ?>
Thank you everyone for input!

Categories