PHP str_replace() in loop only replaces one item - php

I'm trying to parse a string with a php function to it will echo an <img> tag for the bbcode style :smiley: string.
It does work put only parses the first item found and then stops the loop.
I need it to parse the whole text and then return it.
I tried the following:
function parse_emoji($string){
$possibleCodes = array('+1','-1','angry','anguished','astonished','bawling','bleep_bloop','blush','cold_swaet','confounded','confused','cookie','cry','dissapointed','dissapointed_relieved','dizzy','expressionless','fearfull','flushed','frown','grey_question','grimace','grin','heart','heartbreak','hushed','innocent','joy','kiss','kissing_blush','kissing_closed_eyes','kissing_smiling_eyes','kissing_wink','lol','love','love','mask','mrgreen','naughty','neutral','no_mouth','open_mouth','pensive','persevere','rage','relaxed','relieved','scream','skull','sleeping','sleepy','smile','smiley','smirk','star','tongue_grin','tongue_wink','triumph','trollface','unamused','warning','weary','wink','worried','yum');
foreach($possibleCodes as $code) {
return str_replace(':'.$code.':', "<img class='smiley' src='".asset_url()."img/emoticons/".$code.".png'>", $string);
}
}
If I then parse $string('this is a smiley :smile: and this one too :smile:') it will insert an image for the first tag but will echo plain :smiley: for the second.

It's because you return a result in the foreach. It will stop the loop and return the result after only one loop occurence.
You should remove the return in the foreach statement :
function parse_emoji($string){
$possibleCodes = array(...);
foreach($possibleCodes as $code) {
$string = str_replace(':'.$code.':', "<img class='smiley' src='".asset_url()."img/emoticons/".$code.".png'>", $string);
}
return $string;
}

Because you're returning after the first replace.... loop over all the individual replacements, and only then return
function parse_emoji($string){
$possibleCodes = array('+1','-1','angry','anguished','astonished','bawling','bleep_bloop','blush','cold_swaet','confounded','confused','cookie','cry','dissapointed','dissapointed_relieved','dizzy','expressionless','fearfull','flushed','frown','grey_question','grimace','grin','heart','heartbreak','hushed','innocent','joy','kiss','kissing_blush','kissing_closed_eyes','kissing_smiling_eyes','kissing_wink','lol','love','love','mask','mrgreen','naughty','neutral','no_mouth','open_mouth','pensive','persevere','rage','relaxed','relieved','scream','skull','sleeping','sleepy','smile','smiley','smirk','star','tongue_grin','tongue_wink','triumph','trollface','unamused','warning','weary','wink','worried','yum');
foreach($possibleCodes as $code) {
$string = str_replace(':'.$code.':', "<img class='smiley' src='".asset_url()."img/emoticons/".$code.".png'>", $string);
}
return $string;
}

Related

PHP: Remove javascript events from html

Is there any way to remove js events like 'onload', 'onclick',... from html elements in PHP?
For example if <a (onclick)="alert('hi')">Link</a> is given, the desired output should be <a>Link</a>.
I did it this way:
$dom = new DOMDocument;
$dom->loadHTML($request->request->get('description'));
$nodes = $dom->getElementsByTagName('*');
foreach($nodes as $node)
{
if ($node->hasAttribute('onload'))
{
$node->removeAttribute('onload');
}
if ($node->hasAttribute('onclick'))
{
$node->removeAttribute('onclick');
}
}
$dom->saveHTML();
However I'm not sure if it's a safe way to that, because if later a new js event will be created the chance that I'll forget to blacklist it is real.
function filterText($value)
{
if(!$value) return $value;
return escapeJsEvent(removeScriptTag($value));
}
function escapeJsEvent($value){
return preg_replace('/(<.+?)(?<=\s)on[a-z]+\s*=\s*(?:([\'"])(?!\2).+?\2|(?:\S+?\(.*?\)(?=[\s>])))(.*?>)/i', "$1 $3", $value);
}
function removeScriptTag($text)
{
$search = array("'<script[^>]*?>.*?</script>'si",
"'<iframe[^>]*?>.*?</iframe>'si");
$replace = array('','');
$text = preg_replace($search, $replace, $text);
return preg_replace_callback("'&#(\d+);'", function ($m) {
return chr($m[1]);
}, $text);
}
echo filterText('<img src=1 href=1 onerror="javascript:alert(1)"></img>');
You should build a Javascript method that does this for you, and can apply it after the body loads, because php code executes at page load and you can't check later in the document if theres other event, until it loads again.

How can I split html value and normal string into different array in php?

Say I have string such as below:
"b<a=2<sup>2</sup>"
Actually its a formula. I need to display this formula on webpage but after b string is hiding because its considered as broken anchor tag. I tried with htmlspecialchars method but it returns complete string as plain text. I am trying with some regex but I can get only text between some tags.
UPDATE:
This seems to work with this formula:
"(c<a) = (b<a) = 2<sup>2</sup>"
And even with this formula:
"b<a=2<sup>2</sup>"
HERE'S THE MAGIC:
<?php
$_string = "b<a=2<sup>2</sup>";
$string = "(c<a) = (b<a) = 2<sup>2</sup>";
$open_sup = strpos($string,"<sup>");
$close_sup = strpos($string,"</sup>");
$chars_array = str_split($string);
foreach($chars_array as $index => $char)
{
if($index != $open_sup && $index != $close_sup)
{
if($char == "<")
{
echo "<";
}
else{
echo $char;
}
}
else{
echo $char;
}
}
OLD SOLUTION (DOESN'T WORK)
Maybe this can help:
I've tried to backslash chars, but it doesn't work as expected.
Then i've tried this one:
<?php
$string = "b&lta=2<sup>2</sup>";
echo $string;
?>
Using &lt html entity it seems to work if i understood your problem...
Let me know
Probably you can give spaces such as :
b < a = 2<sup>2</sup>
It does not disappear the tag and looks much more understanding....
You could try this regex approach, which should skip elements.
$regex = '/<(.*?)\h*.*>.+<\/\1>(*SKIP)(*FAIL)|(<|>)/';
$string = 'b<a=2<sup>2</sup>';
$string = preg_replace_callback($regex, function($match) {
return htmlentities($match[2]);
}, $string);
echo $string;
Output:
b<a=2<sup>2</sup>
PHP Demo: https://eval.in/507605
Regex101: https://regex101.com/r/kD0iM0/1

Replace multiple items between tags in a string

Trying to write a function that will replace #something# and #anything# in any string with items in my db that match the name "something" and "anything".
This should work for no matter how many different #some-name# there are in my string. Below is what I have so far and it's working, although only the last (#anything#) is being replaced with the correct code when I load in my browser.
Please keep in mind that I'm learning, so I may be completely off on how to go about this. If there is a better way, I'm all ears.
HTML (String)
<p>This is "#something#" I wanted to replace with code from my database. Really, I could have "#anything#" between my pound sign tags and it should be replaced with text from my database</p>
OUTPUT I'm Getting
This is "#something#" I want to replace with code from my database. Really, I could have "Any Name" between my pound sign tags and it should be replaced with text from my database
DESIRED OUTPUT
This is "The Code" I want to replace with code from my database. Really, I could have "Any Name" between my pound sign tags and it should be replaced with text from my database
FUNCTION in CMS class a.php
public function get_snippets($string) {
$regex = "/#(.*?)#/";
preg_match_all($regex, $string, $names);
$names = $names[1];
foreach ($names as $name){
$find_record = Snippet::find_snippet_code($name);
$db_name = $find_record->name;
if($name == $db_name) {
$snippet_name = "/#".$name."#/";
$code = $find_record->code;
}
}
echo preg_replace($snippet_name, $code, $string);
}
FUNCTION in Snippet class b.php
public static function find_snippet_code($name) {
global $database;
$result_array = static::find_by_sql("SELECT * from ".static::$table_name." WHERE name = '{$name}'");
return !empty($result_array) ? array_shift($result_array) : false;
}
It's because your preg_replace occurs outside of the foreach() loop, so it only happens once.
Here is a working example based on your code which returns $string.
Note that I also use PREG_SET_ORDER which gives me each match as its own array:
function get_snippets($string) {
$regex = '/#([^#]+)#/';
$num_matches = preg_match_all($regex, $string, $matches, PREG_SET_ORDER);
if ($num_matches > 0) {
foreach ($matches as $match) {
// Each match is an array consisting of the token we matched and the 'name' without the special characters
list($token, $name) = $match;
// See if there is a matching record for 'name'
$find_record = Snippet::find_snippet_code($name);
// This step might be redundant, but compare name with the record name
if ($find_record->name == $name) {
// Replace all instances of #token# with the code from the matched record
$string = preg_replace('/'.$token.'/', $find_record->code, $string);
}
}
}
return $string;
}
What you're looking for is preg_replace_callback():
public function get_snippets($string)
{
$regex = "/#(.*?)#/";
return preg_replace_callback($regex, function($match) {
$find_record = Snippet::find_snippet_code($match[1]);
return $find_record === false ? '' : $find_record->code;
}, $string);
}

Create and simplify links from body of text in PHP

I need to extract all links in a body of text in php and make them clickable. The problem is I can't seem to simplify the text of the link in any way.
I tried using preg_replace_callback but I can't seem to get the trimming function working properly:
function trimUrl($url){
$maxLength = 3;
if(strlen($url)>$maxLength){
$urlShort = substr($str,0,$maxLength).'...';
}
else{
$urlShort = $url;
}
return $urlShort;
}
function enableLinks($text){
return preg_replace_callback("!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9#:%_+.~#?&;//=]+)!i", "<a href='$1' target='_blank'>".trimUrl("$1")."</a>", $text);
}
enableLinks("Visit more work at http://www.google.com");
How can I run a second function within the preg_replace_callback that trims the output text?
What if you used a function inside that function. So if the first function evaluates to true then run this next function? And also try using preg_replace_callback in a variable format so its easier to work with
First, you are using substring(). Where have you defined the variable $str? And, if you do this:
$var = preg_replace_callback("!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9#:%_+.~#?&;//=]+)!i", "<a href='$1' target='_blank'>".trimUrl("$1")."</a>", $text);
Than can you use a new function:
return function($var);
Ended up using a more expanded function to achieve this, works on multiple urls with or without "http://":
function trimUrlOutput($url){
$maxLength = 30;
if(strlen($url)>$maxLength){
$urlShort = substr($url,0,$maxLength).'...';
}
else{
$urlShort = $url;
}
return $urlShort;
}
function enableLinks($text){
$text = ereg_replace( "www\.", "http://www.", $text );
$text = ereg_replace( "http://http://www\.", "http://www.", $text );
$text = ereg_replace( "https://http://www\.", "https://www.", $text );
$reg_exUrl = "/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/";
if(preg_match_all($reg_exUrl, $text, $url)) {
$matches = array_unique($url[0]);
foreach($matches as $match) {
$linkText = trimUrlOutput($match);
$replacement = "<a href=".$match." target='_blank'>{$linkText}</a>";
$text = str_replace($match,$replacement,$text);
}
return $text;
}
else{
return $text;
}
}
enableLinks("Visit more work at http://www.google.com");
Hope this helps someone.

Remove the first paragraph tags from string

String
"<p>This is </p><p>Stackoverflow</p><p>site for programmers</p>"
Required Output
"This is <p>Stackoverflow</p><p>site for programmers</p>"
Small function
function remove_p($string)
{
$first_p=substr($string,0,3);
$p="<p>";
if($first_p==$p)
{
$string=str_replace('<p>','',$string,$temp=1);
$string=str_replace('</p>','',$string,$temp=1);
}
return $string;
}
But it removes all the <p> </p> tags.Why so?
I am basically writing this to remove the first paragraph tags created by ckeditor.
str_replace acts on all occurrences of a substring, not just the first. You will want to use a different function.
$string = preg_replace('~<p>(.*?)</p>~is', '$1', $string, /* limit */ 1);
To only remove the first <p> and </p> if at the start of the string, add a ^ after the first /.
See also: Using str_replace so that it only acts on the first match?
function replaceFirst($input, $search, $replacement){
$pos = stripos($input, $search);
if($pos === false){
return $input;
}
else{
$result = substr_replace($input, $replacement, $pos, strlen($search));
return $result;
}
}
$string = "This is <p>Stackoverflow</p><p>site for programmers</p>";
echo $string;
echo replaceFirst($string, '<p>', '');
Output:
This is <p>Stackoverflow</p><p>site for programmers</p>
This is Stackoverflow</p><p>site for programmers</p>
Source: #2031045
Hope this helps!
$str = "This is <p>Stackoverflow</p><p>site for programmers</p>";
function remove_p($string)
{
$string=str_replace('<p>','',$string,$temp=1);
$string=str_replace('<\p>','',$string,$temp=1);
return $string;
}
echo(remove_p($str));
The result is:
This is Stackoverflow
site for programmers
Try using the method of this answer.
function remove_p($string)
{
return replaceFirst(replaceFirst($string, '<p>', ''), '</p>', '');
}
Or read about Regular Expressions.

Categories