preg_replace : how get what is replaced - php

I want to show cards from regex codes :
ah displays As of hearts,
kc displays King of clubs
...
I used preg_replace() to do that in this way :
$arr = array('ah', 'kh', 'qh', ..., '3c', '2c');
$regex = '';
foreach ($arr as $i => $card)
{
$regex .= $card;
if ($i < count($arr) - 1)
$regex .= '|';
}
$message = preg_replace('#('.$regex.')#', '<img src="'.$dontknow.'.png" class="card" alt="" />', $message);
I don't know what value put in the src attribute, I want to tell to preg_replace() "when you find 'ah' you put ah.png, if it's kc then $dontknow == 'kc' etc.
Someone could be bring me some help ?

You can do this:
$message = preg_replace('#('.$regex.')#',
'<img src="$1.png" class="card" alt="" />', $message);

Use $1 reference - it is a link to a first group that PHP matched through preg_replace

You just need to use $n in your replacement to reference to a certain matching group (n is a number).
Since we pulled out the big guns:
Let's use preg_quote() to escape regex reserved characters in your array
PHP has a great set of function to juggle with arrays, let's use implode() instead of that ugly loop
From the comments, I realised that you need to add word boundaries \b to prevent false matches like yeah being replaced to ye<img...>. See this demo
Code:
$message = 'foo qh bar';
$arr = array('ah', 'kh', 'qh', '3c', '2c');
$escaped_arr = array_map(function($v){
return preg_quote($v, '#');
}, $arr); // Anonymous function requires PHP 5.3+
$message = preg_replace('#\b('.implode('|', $escaped_arr).')\b#', '<img src="$1.png" class="card" alt="" />', $message);
echo $message;
Online demo

You don't need that for loop. Here's a slightly improved version with the correct regex.
$arr = array('ah', 'kh', 'qh', ..., '3c', '2c');
$message = preg_replace('/('. implode('|'. $regex) .')/is', '<img src="$1.png" class="card" alt="" />', $message);

You should improve your 'markers' indicating the playing cards, by adding a special character (like # in the following example) to them. This will protect you from inadvertently changing other text passages:
$arr = array('ah', 'kh', 'qh', 'ac', '3c', '2c');
$txt = 'This is a line with cards #ah and #qh but with a potential head-ache.';
$rx = '##('.join('|',$arr).')#';
echo preg_replace($rx,"<img src=$1.jpg>",$txt);

Related

Regex replace keyword with different string by loop (PHP)

Let say I have a string
$content = "hello . wow . cool . yes!";
It's easy to just replace the dot to another string:
echo preg_replace("/\./", "<img>", $content);
so the output is:
hello <img> wow <img> cool <img> yes!
however, is there any way to replace it one by one?
for example, I have an array and would like to insert them into the string.
$arr = ['<img src="a"/>', '<img src="b"/>', '<img src="c"/>'];
the expected output:
hello <img src="a"/> wow <img src="b"/> cool <img src="c"/> yes!
I can use preg_match_all to get the separator but still have no idea how to replace it respectively.
Thank you for any help.
Use preg_replace_callback. Store the number of the current full stop in a static variable. Access $arr from the callback with use (). Use modulo in case there are not enough items in $arr.
<?php
$content = 'hello . wow . cool . yes!';
$arr = ['<img src="a"/>', '<img src="b"/>', '<img src="c"/>'];
$content = preg_replace_callback ('/\./', function ($_) use ($arr) {
static $count = 0;
return $arr [$count++ % count ($arr)];
}, $content);
echo ($content);
http://sandbox.onlinephpfunctions.com/code/f9115bdb6eb17e30012d987ed1fc4b95e7c10d33.

how to replace string with another one by one with preg_replace

I have this code and my question is how can I use preg_replace to replace string one by one
$arr = ["{A}","{B}","{C}","{A}"];
$string = "{A}{B}{C}{A}";
foreach ($arr as $item){
$replacement = "<span class=\"c\">{$item}</span>";
$new_String = preg_replace("/$item/",$replacement ,$string);
}
the result is this :
<span class="c">
<span class="c">{A}</span>
</span>
<span class="c">{B}</span>
<span class="c">{C}</span>
<span class="c">
<span class="c">{A}</span>
</span>
Because I have 2 {A} in my string preg_replace make 2 span for both of the {A} .
how to fix this ?
You are using preg_replace a wrong way: the main interest of this function is to take a regex pattern as parameter, and a pattern isn't to describe a fixed string but can describe several kind of strings (and this way you don't have to use a foreach loop since you can replace the whole string in 1 pass), example:
$result = preg_replace('/{[A-G]}/', '<span class="c">$0</span>', $string);
Other way, since you want to replace only fixed strings, you can use strtr that also does the job in one pass:
$arr = ["{A}","{B}","{C}"];
$rep = array_map(function($i) { return '<span class="c">' . $i . '</span>'; }, $arr);
$trans = array_combine($arr, $rep);
$result = strtr($string, $trans);
In addition to Casimir's suggested snippets, you can also use this one:
Code: (Demo)
$arr = ["{A}", "{B}", "{C}", "{A}"];
$start = '<span class="c">';
$end = '</span>';
$string = "{A}{B}{C}{A}";
foreach (array_unique($arr) as $item) {
$string = str_replace($item, $start . $item . $end, $string);
}
echo $string;
It alters how you define the replacement, but avoids regex.
As a more robust, solution (if your project requires it), using strtr() is ideal for multiple replacements on the same string because it avoid replacing a replacement. As you can see in Casimir's answer, there's a bit of preparation required.
Here's a language-construct version of the Casimir's technique: (Demo)
$arr = ["{A}", "{B}", "{C}", "{A}"];
foreach (array_unique($arr) as $item) {
$translator[$item] = '<span class="c">' . $item . '</span>';
}
var_export($translator);
echo "\n\n---\n\n";
$string = "{A}{B}{C}{A}";
echo strtr($string, $translator);
Output:
array (
'{A}' => '<span class="c">{A}</span>',
'{B}' => '<span class="c">{B}</span>',
'{C}' => '<span class="c">{C}</span>',
)
---
<span class="c">{A}</span><span class="c">{B}</span><span class="c">{C}</span><span class="c">{A}</span>

issus with emoji in array

In this code, when I use ":-)" emoji doesn't show in output.
But when use "1f60a" OR "1f60c" OR "e252" emoji are shown. What's the problem?
<?php
$emoji_url = "http://coremobile.ir/images_smileys";
$emoji_style = "";
$emoji_code = array(
":-)",
"1f60a",
"1f60c",
"e252"
);
$emoji_img = array(
'<img src="'.$emoji_url.'/1f60a.png" '.$emoji_style.'>',
'<img src="'.$emoji_url.'/1f60a.png" '.$emoji_style.'>',
'<img src="'.$emoji_url.'/1f60c.png" '.$emoji_style.'>',
'<img src="'.$emoji_url.'/e252.png" '.$emoji_style.'>'
);
$ret = 'This Test :-) 1f60a';
$ret = str_replace($emoji_code, $emoji_img, $ret);
echo $ret;
?>
This should work for you:
(Just use strtr() instead of str_replace(), so that it won't go through the string multiple times)
$ret = strtr($ret, array_combine($emoji_code, $emoji_img));
output:
This Test
The otherone didn't worked, because it replaced every match for the first replacement and then the second and so on.
0 replaced:
This Test :-) 1f60a
//^^^ match
first replaced:
This Test <img src="http://coremobile.ir/images_smileys/1f60a.png" > 1f60a
//^^^^^ match ^^^^^ match
second replaced:
This Test <img src="http://coremobile.ir/images_smileys/<img src="http://coremobile.ir/images_smileys/1f60a.png" >.png" > <img src="http://coremobile.ir/images_smileys/1f60a.png" >

bbCode in an another

I made a custom function which acts like bbCode. I'm using preg_replace and regex. The only problem is that if I use more than one bbCode formatting, then just only one works..
[align=center][img]myimagelink[/img][/align]
If I enter this line, then the image appears BUT the [align=center]image[/align] also. How can I avoid this problem?
$patterns[2] = '#\[align=(.*)\](.*)\[\/align\]#si';
$patterns[9] = '#\[img\](.*\.jpg)\[\/img\]#si';
$replacements[2] = '<table align=\1><tr><td align=\1>\2</td></tr></table>';//ALIGN
$replacements[9] = '<img src=\"$1\"/>';//image
Changing the .* expressions to non-greedy (.*?) will work for you.
Example:
$in = '[align=center][img]myimagelink[/img][/align]';
$patterns = array(
'~\[align=(left|right|center)\](.*?)\[/align\]~' => '<div style="text-align: $1">$2</div>',
'~\[img](.*?)\[/img\]~' => '<img src="$1" />',
);
$rep = preg_replace(array_keys($patterns), $patterns, $in);
echo htmlspecialchars($rep);
Rather than reinventing the wheel I recommend using an existing javascript library.
I believe StackOverflow uses Prettify to format user input.
As #nickb stated, your patterns are greedy. (.*) grabs everything. Try changing it to (.*?).
treat all tags as singles not pairs
$patterns[2] = '#\[align=(.*)\]#si';
$patterns[3] = '#\[\/align\]#si';
$patterns[9] = '#\[img\](.*\.jpg)\[\/img\]#si';
$replacements[2] = '<div align=\"$1\">';//ALIGN
$replacements[3] = '</div>';//ALIGN
$replacements[9] = '<img src=\"$1\"/>';//image

Smiley Replace within CDATA of an HTML-String

i have got a simple problem :( I need to replace text smilies with the according smiley-image. ok.. thats not really complex, but now i have to replace only smilie appereances outside of HTML Tags. short examplae:
Text:
Thats a good example :/ .. with a link inside.
i want to replace ":/" with the image of this smiley...
ok, how to do that the best way?
I won't try to create some super script but think about it.... smilies are just about always surrounded by spaces. So str replace ' :/ ' with the smiley. You could be saying "what about a smiley at the end of a sentence(where it would be used the most)". Well just check for at least one space on either the left or the right of a potential smiley.
Using the above scripts:
$smiley_array = array(
":) " => "<a href...>",
" :)" => "<a href...>",
":/ " => "<a href...>",
" :/" => "<a href...>");
$codes = array_keys($smiley_array);
$links = array_values($smiley_array);
$str = str_replace($codes, $links, $str);
If you rather not have to type everything twice you can generate the array from a single smiley array.
Why don't you just try to use some special chars around your smiley text like this maybe -:/-
This will make your smiley text some kind of unique and easy to recognize
Use preg_replace with a lookbehind assertion. Example:
$smileys = array(
':/' => '<img src="..." alt=":/">'
);
foreach ($smileys as $smile => $img) {
$text = preg_replace('#(?<!<[^<>]*)' . preg_quote($smile, '#') . '#',
$img, $text);
}
The regex should match only smileys that are not inside angle brackets. This might be slow if you have a lot of false positives.
I wouldn't know about the best way, only the way I would do it.
Build an array having the smiley codes as the keys and the link as the value. The use str_replace. Pass as "needle" an array of the keys (the smiley codes) and as "replace" an array of the values.
For instance, suppose you have something like this:
$smiley_array = array(":)" => "<a href...>",
":(" => "<a href=....>");
$codes = array_keys($smiley_array);
$links = array_values($smiley_array);
$str = str_replace($codes, $links, $str);
EDIT: In case this could accidentally replace other instances with smiley-links you should consider using regexes with preg_replace. Obviously preg_replace is slower than str_replace.
You can use regex, or the extra sloppy version of the above:
$smiley_array = array(":)" => "<a href...>",
":(" => "<a href=....>");
$codes = array_keys($smiley_array);
$links = array_values($smiley_array);
$str = str_replace("://", "%%QF%%", $str);
$str = str_replace($codes, $links, $str);
$str = str_replace("%%QF%%", "://", $str);
Actually, assuming str_replace follows the array sorting...
this should work:
$smiley_array = array("://" => "%%QF%%", ":)" => "<a href...>",
":(" => "<a href=....>", "%%QF%%" => "://");
$codes = array_keys($smiley_array);
$links = array_values($smiley_array);
$str = str_replace($codes, $links, $str);
Possible overkill (increased cpu/load), but 99.99999999% safe:
<?php
$n = new DOMDocument();
$n->loadHTML('<p>Thats a good example :/ .. with a link inside.</p>');
$x = new DOMXPath($n);
$instances = $x->query('//text()[contains(.,\':/\')]');//or use '//*[child::text()]' for all textnodes
foreach($instances as $node){
if($node instanceof DOMText && preg_match_all('/:\//',$node->wholeText,$matches,PREG_OFFSET_CAPTURE|PREG_SET_ORDER)){
foreach($matches[0] as $match){
$newnode = $node->splitText($match[1]);
$newnode->replaceData(0,strlen($match[0]),'');
$img = $n->createElement('img');
$img->setAttribute('src','smily.gif');
$img = $newnode->parentNode->insertBefore($img,$newnode);
//var_dump($match);
}
}
}
var_dump($n->saveHTML());
?>
But in reality you do not want to do this all that often, save once, show many, if you are letting users edit the html (beit in wysiwyg or elsewise, the 'return' transformation (img to text) is a whole lot lighter. Up to you to expand with different smilies (one monster regex to match them, or several smaller ones / strstr()'s for readability, and a array for smiley to src (e.g. array(':/'=>'frown.gif')) would be the way to go.

Categories