preg_replace causing dollar signs get removed - php

I have an email system, where user write a message and it will send the message.
The main problem which I just found, consider this code
$findEmail = $this->Data->field('body', array('id' => 1610));
//$getUserEmailTemplate will take frm dbase and e.g:
//Hi, ##MESSAGE##. From: StackOverflow
//It should change ##MESSAGE## part to data from $findEmail (in this example is the $74.97 ...)
$getUserEmailTemplate = $findUser['User']['email_template'];
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', $findEmail, $getUserEmailTemplate);
debug($findEmail);
debug($emailMessage);
and consider this input for the email for $findemail result:
$74.97
$735.00s
$email Message will result in:
.97
5.00s
How can I fix this? I feel like there's problem with my preg_replace pattern.
User template can be anything, as long as there is ##MESSAGE## which, that part will be changed to the user message input.
Thank you

Pre-parse the replacement text to escape the $ when followed by a number (remember that $n has special meaning when using in the replacement text). See the comment on the php.net docs page:
If there's a chance your replacement text contains any strings such as
"$0.95", you'll need to escape those $n backreferences:
<?php
function escape_backreference($x){
return preg_replace('/\$(\d)/', '\\\$$1', $x);
}
?>

The high-voted function escape_backreference is incomplete in the general case: it will only escape backreferences of the form $n, but not those of the form ${n} or \n.
To escape any potential backreferences, change
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', $findEmail, $getUserEmailTemplate);
to
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', addcslashes($findEmail, '\\$'), $getUserEmailTemplate);

Here is the reason:
The $1 portion of a replacement text stands for the first group/match found. So if you have abc 123 and you try preg_match('/([\w]+)-([\d]+)/'), regex will store internally something like $1 = abc and $2 = 123. Those variables are going to exists, even if they have no value.
So, for example:
$text = '[shortcode]';
$replacement = ' some $var $101 text';
$result = preg_replace('/\[shortcode\]/', $var, $text);
// returns "some $var 1 text"
As the match group $10 is empty is going to be replaced by a null string.
That's why you need to scape any $NN from your REPLACEMENT text before running the preg_replace function.
Happy coding.

If (ever) an template hast been in $getUserEmailTemplate, you did overwrite (destroy) it with this line;
$getUserEmailTemplate = "##MESSAGE##";
So just remove this line and make sure, $getUserEmailTemplate really contains anything and best of all a template.

Guess your template just includes "pure" PHP and tries to use $74 as variable, which does not exist and does not hold any data. So change the quotes in the template to single quotes '.
guessed template:
$tpl = "Sum: $74.97"; //results in "Sum: .97"
corrected template:
$tpl = 'Sum: $74.97'; //results in "Sum: $74.97"

Related

Preg_replace not working properly for $(dollar) symbol in php [duplicate]

I have an email system, where user write a message and it will send the message.
The main problem which I just found, consider this code
$findEmail = $this->Data->field('body', array('id' => 1610));
//$getUserEmailTemplate will take frm dbase and e.g:
//Hi, ##MESSAGE##. From: StackOverflow
//It should change ##MESSAGE## part to data from $findEmail (in this example is the $74.97 ...)
$getUserEmailTemplate = $findUser['User']['email_template'];
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', $findEmail, $getUserEmailTemplate);
debug($findEmail);
debug($emailMessage);
and consider this input for the email for $findemail result:
$74.97
$735.00s
$email Message will result in:
.97
5.00s
How can I fix this? I feel like there's problem with my preg_replace pattern.
User template can be anything, as long as there is ##MESSAGE## which, that part will be changed to the user message input.
Thank you
Pre-parse the replacement text to escape the $ when followed by a number (remember that $n has special meaning when using in the replacement text). See the comment on the php.net docs page:
If there's a chance your replacement text contains any strings such as
"$0.95", you'll need to escape those $n backreferences:
<?php
function escape_backreference($x){
return preg_replace('/\$(\d)/', '\\\$$1', $x);
}
?>
The high-voted function escape_backreference is incomplete in the general case: it will only escape backreferences of the form $n, but not those of the form ${n} or \n.
To escape any potential backreferences, change
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', $findEmail, $getUserEmailTemplate);
to
$emailMessage = preg_replace('/\B##MESSAGE##\B/u', addcslashes($findEmail, '\\$'), $getUserEmailTemplate);
Here is the reason:
The $1 portion of a replacement text stands for the first group/match found. So if you have abc 123 and you try preg_match('/([\w]+)-([\d]+)/'), regex will store internally something like $1 = abc and $2 = 123. Those variables are going to exists, even if they have no value.
So, for example:
$text = '[shortcode]';
$replacement = ' some $var $101 text';
$result = preg_replace('/\[shortcode\]/', $var, $text);
// returns "some $var 1 text"
As the match group $10 is empty is going to be replaced by a null string.
That's why you need to scape any $NN from your REPLACEMENT text before running the preg_replace function.
Happy coding.
If (ever) an template hast been in $getUserEmailTemplate, you did overwrite (destroy) it with this line;
$getUserEmailTemplate = "##MESSAGE##";
So just remove this line and make sure, $getUserEmailTemplate really contains anything and best of all a template.
Guess your template just includes "pure" PHP and tries to use $74 as variable, which does not exist and does not hold any data. So change the quotes in the template to single quotes '.
guessed template:
$tpl = "Sum: $74.97"; //results in "Sum: .97"
corrected template:
$tpl = 'Sum: $74.97'; //results in "Sum: $74.97"

Understanding the eval() function of php

I'm a designer trying to upgrade myself into a coder-designer. Lately I've been looking into some PHP codes and manuals, then I ran into an example code for the eval() function :
<?php
$string = 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo $str. "\n";
eval("\$str = \"$str\";");
echo $str. "\n";
?>
This is an example code of eval() function in official PHP website, and although it did help me understand the eval() function, I can't figure out how the example code works. to be more specific, I can't understand why
("\$str = \"$str\";")
results in a merged string.
I really can't figure out why this should work.
Ok, here is what we have:
eval("\$str = \"$str\";")
Look, the string is in double quotes: it means that the $ character will be interpreted as a variable start. So we screen this character with a backslash: \$, then it will "mean" just a normal dollar sign. Also, the double quotes inside the string had to be screened too.
In the end we are getting this string: (I changed the quotes to single so $ dont confuse you): '$str = "$str";'. Look, it looks more like a normal code now :)
Evaling it, PHP will do the following (I removed the outer quotes for convenience):
eval( $str = "$str" );
Notice the double quotes here, too. It means that the variable inside, again, will be parsed/interpreted.
As $str was originally == 'This is a $string with my $name in it.', it will be inserted into the expression, and now it will look like:
$str = "This is a $string with my $name in it.";
And, again, double quotes! It parses and substitutes variables $name and $string, giving us at the end:
$str = "This is a cup with my coffee in it."
Voila!
A mindbreaker, but a really good example to learn the mechanisms.
you should get two console.log like this
This is a $string with my $name in it.';
This is a $cup with my $coffe in it.';
Why? well first you print the value, of $str , without eval, and later, you eval them, basically this happens,
First.
Print $str, without eval.
This is a $string with my $name in it.';
Second
this piece of code, runs eval("\$str = \"$str\";");
Eval replace $string and $name. with the 2 new variables values, which are $cup and $coffe
Hope you get it

Erasing C comments with preg_replace

I need to erase all comments in $string which contains data from some C file.
The thing I need to replace looks like this:
something before that shouldnt be replaced
/*
* some text in between with / or * on many lines
*/
something after that shouldnt be replaced
and the result should look like this:
something before that shouldnt be replaced
something after that shouldnt be replaced
I have tried many regular expressions but neither work the way I need.
Here are some latest ones:
$string = preg_replace("/\/\*(.*?)\*\//u", "", $string);
and
$string = preg_replace("/\/\*[^\*\/]*\*\//u", "", $string);
Note: the text is in UTF-8, the string can contain multibyte characters.
You would also want to add the s modifier to tell the regex that .* should include newlines. I always think of s to mean "treat the input text as a single line"
So something like this should work:
$string = preg_replace("/\\/\\*(.*?)\\*\\//us", "", $string);
Example: http://codepad.viper-7.com/XVo9Tp
Edit: Added extra escape slashes to the regex as Brandin suggested because he is right.
I don't think regexp fit good here. What about wrote a very small parse to remove this? I don't do PHP coding for a long time. So, I will try to just give you the idea (simple alogorithm) I haven't tested this, it's just to you get the idea, as I said:
buf = new String() // hold the source code without comments
pos = 0
while(string[pos] != EOF) {
if(string[pos] == '/') {
pos++;
while(string[pos] != EOF)
{
if(string[pos] == '*' && string[pos + 1] == '/') {
pos++;
break;
}
pos++;
}
}
buf[buf_index++] = string[pos++];
}
where:
string is the C source code
buf a dynamic allocated string which expands as needed
It is very hard to do this perfectly without ending up writing a full C parser.
Consider the following, for example:
// Not using /*-style comment here.
// This line has an odd number of " characters.
while (1) {
printf("Wheee!
(*\/*)
\\// - I'm an ant!
");
/* This is a multiline comment with a // in, and
// an odd number of " characters. */
}
So, from the above, we can see that our problems include:
multiline quote sequences should be ignored within doublequotes. Unless those doublequotes are part of a comment.
single-line comment sequences can be contained in double-quoted strings, and in multiline strings.
Here's one possibility to address some of those issues, but far from perfect.
// Remove "-strings, //-comments and /*block-comments*/, then restore "-strings.
// Based on regex by mauke of Efnet's #regex.
$file = preg_replace('{("[^"]*")|//[^\n]*|(/\*.*?\*/)}s', '\1', $file);
try this:
$string = preg_replace("#\/\*\n?(.*)\*\/\n?#ms", "", $string);
Use # as regexp boundaries; change that u modifier with the right ones: m (PCRE_MULTILINE) and s (PCRE_DOTALL).
Reference: http://php.net/manual/en/reference.pcre.pattern.modifiers.php
It is important to note that my regexp does not find more than one "comment block"... Use of "dot match all" is generally not a good idea.

Regex to match double quoted strings without variables inside php tags

Basically I need a regex expression to match all double quoted strings inside PHP tags without a variable inside.
Here's what I have so far:
"([^\$\n\r]*?)"(?![\w ]*')
and replace with:
'$1'
However, this would match things outside PHP tags as well, e.g HTML attributes.
Example case:
Here's my "dog's website"
<?php
$somevar = "someval";
$somevar2 = "someval's got a quote inside";
?>
<?php
$somevar3 = "someval with a $var inside";
$somevar4 = "someval " . $var . 'with concatenated' . $variables . "inside";
$somevar5 = "this php tag doesn't close, as it's the end of the file...";
it should match and replace all places where the " should be replaced with a ', this means that html attributes should ideally be left alone.
Example output after replace:
Here's my "dog's website"
<?php
$somevar = 'someval';
$somevar2 = 'someval\'s got a quote inside';
?>
<?php
$somevar3 = "someval with a $var inside";
$somevar4 = 'someval ' . $var . 'with concatenated' . $variables . 'inside';
$somevar5 = 'this php tag doesn\'t close, as it\'s the end of the file...';
It would also be great to be able to match inside script tags too...but that might be pushing it for one regex replace.
I need a regex approach, not a PHP approach. Let's say I'm using regex-replace in a text editor or JavaScript to clean up the PHP source code.
tl;dr
This is really too complex complex to be done with regex. Especially not a simple regex. You might have better luck with nested regex, but you really need to lex/parse to find your strings, and then you could operate on them with a regex.
Explanation
You can probably manage to do this.
You can probably even manage to do this well, maybe even perfectly.
But it's not going to be easy.
It's going to be very very difficult.
Consider this:
Welcome to my php file. We're not "in" yet.
<?php
/* Ok. now we're "in" php. */
echo "this is \"stringa\"";
$string = 'this is \"stringb\"';
echo "$string";
echo "\$string";
echo "this is still ?> php.";
/* This is also still ?> php. */
?> We're back <?="out"?> of php. <?php
// Here we are again, "in" php.
echo <<<STRING
How do "you" want to \""deal"\" with this STRING;
STRING;
echo <<<'STRING'
Apparently this is \\"Nowdoc\\". I've never used it.
STRING;
echo "And what about \\" . "this? Was that a tricky '\"' to catch?";
// etc...
Forget matching variable names in double quoted strings.
Can you just match all of the string in this example?
It looks like a nightmare to me.
SO's syntax highlighting certainly won't know what to do with it.
Did you consider that variables may appear in heredoc strings as well?
I don't want to think about the regex to check if:
Inside <?php or <?= code
Not in a comment
Inside a quoted quote
What type of quoted quote?
Is it a quote of that type?
Is it preceded by \ (escaped)?
Is the \ escaped??
etc...
Summary
You can probably write a regex for this.
You can probably manage with some backreferences and lots of time and care.
It's going to be hard and your probably going to waste a lot of time, and if you ever need to fix it, you aren't going to understand the regex you wrote.
See also
This answer. It's worth it.
Here's a function that utilizes the tokenizer extension to apply preg_replace to PHP strings only:
function preg_replace_php_string($pattern, $replacement, $source) {
$replaced = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)){
$replaced .= $token;
continue;
}
list($id, $text) = $token;
if ($id === T_CONSTANT_ENCAPSED_STRING) {
$replaced .= preg_replace($pattern, $replacement, $text);
} else {
$replaced .= $text;
}
}
return $replaced;
}
In order to achieve what you want, you can call it like this:
<?php
$filepath = "script.php";
$file = file_get_contents($filepath);
$replaced = preg_replace_php_string('/^"([^$\{\n<>\']+?)"$/', '\'$1\'', $file);
echo $replaced;
The regular expression that's passed as the first argument is the key here. It tells the function to only transform strings to their single-quoted equivalents if they do not contain $ (embedded variable "$a"), { (embedded variable type 2 "{$a[0]}"), a new line, < or > (HTML tag end/open symbols). It also checks if the string contains a single-quote, and prevents the replacement to avoid situations where it would need to be escaped.
While this is a PHP solution, it's the most accurate one. The closest you can get with any other language would require you to build your own PHP parser in that language to some degree in order for your solution to be accurate.

Get a number after a character in a string

I'm making my own forums and I don't want any BB code on it, but instead my own,
so i've gotten [b][u][img] working etc.
But i'm having problems with [quote=1][/quote] where the number is the user id...
E.G lets say I quote someone
So once I submit my post: (The variable $post would be:)
'[quote=1] Quoted post :P[/quote]'
How would I then get the number out the string? (But not the wrong number -not a number in the quoted post)
(So I could then use str_replace() to replace with a table which makes it looked quoted)
?? :)
\[quote=([0-9]*)\] and grab the captured string $1
$pattern = "{\[quote=([0-9]*)\](.*)\[\/quote\]}";
$subject = $post;
preg_match($pattern, $subject, $matches);
//$matches[0] contains the whole string
//$matches[1] contains the id
It is very common to use regular expressions in order to implement BB-Codes. Of course you could use something like str_replace, but you will probably get some problems later.
Use the following pattern to make sure that the quote-tag gets also closed:
/\[quote=(\d+)\](.*?)\[\/quote\]/is
Now you should use preg_replace or preg_match to work with it.
For example:
echo preg_replace('/\[quote=(\d+)\](.*?)\[\/quote\]/is',
'<b>\\1 wrote:</b> \\2',
$input
);
Or:
$input = "text [quote=11]my quoted post
abc[/quote]
[quote=20]my quoted post 2[/quote]";
if(preg_match_all('/\[quote=(\d+)\](.*?)\[\/quote\]/is', $input, $matches)) {
var_dump($matches);
}
This should work for you.
$post = '[quote=1] Quoted post :P[/quote]';
if (preg_match("/\\[quote=([\d]+)\\]/",$post,$matches)) {
//echo "<pre>".print_r($matches,true)."</pre>";
$quote_user = $matches[1];
}

Categories