PHP: run function at certain places (preg_callback?) - php

I'm trying to run a function whenever there's a [%xxx%] (acting as a placeholder, if you will), e.g.:
Bla bla bla blabla. Blablabla bla bla.
[%hooray%]
Blabla hey bla bla. [%yay%] blabla bla.
I'm pretty much a PHP beginner, but I managed to crack my head through and come up with the following (yay to me - I somehow managed to understand the basics of regular expressions!):
$maintext = preg_replace("#\[%(.{1,20})%\]#", "display_products('$1')"), $maintext);
echo $maintext;
I tried working with the e modifier, I tried using preg_replace_callback, and it all does somewhat work - but I don't need the function to run before I echo the $maintext variable.
Any help on this guys?
edit
The regex isn't the issue - that's working fine. I just need to figure out how to run the function only when I echo $maintext... preg_replace_callback runs the function immediately...

Because PHP is a single threaded procedural scripting language1, it doesn't work in the way you want it to.
It is not possible to trigger the function to run only when you call echo2, what you need instead is an extra variable.
$newVariable = preg_replace_callback("#\[%(.{1,20})%\]#", function($matches) {
// Do your thang here
}, $maintext);
// $maintext remains the same as it was when you started, do stuff with it here
// When you come to output the data, do...
echo $newVariable;
There are other approaches to this problem, like wrapping the above code in a function that can be called on demand and using the output buffering callback (see footnote #2), but reading between the lines I think all of this would be overkill for what is a relatively simple problem.
For the record, I think your regex would be better if you narrow the allowed content down a it, it will potentially match characters you don't want it to. I suspect that this would be better:
#\[%(\w{1,20})%\]#
This will allow only the characters a-zA-Z0-9_ in the placeholder.
1 Others may tell you PHP is now an object oriented language, but they are wrong. It still fundamentally works the same underneath and its still a scripting language, but now provides many OO-features.
2 It is possible to do something very close to this, but it is very rarely ever a good idea and is far too advanced for dealing with this problem.
EDIT
It is important to note that when using either method to pass replacements through a callback (e modifier or preg_replace_callback()) the callback function should return the new string, rather than outputting it directly.
This is because the code in any statement is executed from the inside out, and the "inner" echo statement (in the callback function) will be executed before the "outer" echo statement (on the line where preg_replace() is called, so the order in which things are output will be incorrect.
For example:
$str = "This is my string which contains a %placeholder%";
echo preg_replace_callback('/%(\w+)%/', function($matches) {
echo "replacement string";
}, $str);
// Wrong - outputs "replacement stringThis is my string which contains a "
echo preg_replace_callback('/%(\w+)%/', function($matches) {
return "replacement string";
}, $str);
// Right - outputs "This is my string which contains a replacement string"

If you have a script that uses echo many times and you want the printed text modified, you can use output buffering:
ob_start();
/*
* Lots of php code
*/
$maintext = ob_get_clean(); // now you capture the content in $maintext
// than you modify the output
$maintext = preg_replace("#\[%(.{1,20})%\]#", "display_products($1)", $maintext);
echo $maintext;

ob_start accepts a callback function, you can read more on this topic :
Making all PHP file output pass through a "filter file" before being displayed

Related

ob_start() not replacing content

<?php
ob_start(function($buffer){
$buffer = preg_replace("/{%(data_.*?)%}/", '<?php echo $data[\'slot_$1\']; ?>', $buffer);
$buffer = preg_replace("/{%menu_(.*?)%}/", '<?php echo insertNav($_data[\'slot_$1\']); ?>', $buffer);
return $buffer;
});
?>
Trying to use the preview code to replace content with php code. Basically, it is for an editor similar to this one on stack overflow where if you type ** strong text ** = strong text except I'm using it to pull data from a database for a particular item. I am using {%data_#%} to get the # and replace it with $data['slot_#'] just for a reference on what I am doing with this.
If I replace <?php echo $data[\'slot_$1\']; ?> with 'Hello' it echoes out Hello. So why isn't it echoing out the php code?
EDIT
I replaced it with $buffer = preg_replace("/{%(data_.*?)%}/", '$1', $buffer); and it echoes out data_1. It isn't getting the # value and placing it into the php code echoed out. The echoed out code appears to be $data['slot_data_#] instead of $data['slot_#']. It is only supposed to get the number when typing in {%data_#%}
EDIT 2
I finally got the number to echo out. Turns out I had a ( in the wrong spot. Here is my new line: $buffer = preg_replace("/{%data_(.*?)%}/", '<?php echo $data[\'slot_$1\']; ?>', $buffer);.... however, it still is leaving everyhting blank. I know that $1 is now echoing out the correct number, but when I put it into the php code, nothing gets echoed out on the page. And I copied and pasted that php code directly in and replaced the number with $1 so that should be right.
<?php
ob_start(function($buffer){
$buffer = preg_replace("/{%data_(.*?)%}/", '?><?php echo $data["slot_$1"]; ?><?php', $buffer);
$buffer = preg_replace("/{%menu_(.*?)%}/", '?><?php echo insertNav($_data["slot_$1"]);?><?php', $buffer);
return eval($buffer);
});
PHP codes within ob_start are not executed in this way, you should work with an evil function: eval()
Over the past few days, I found that using ob_start() to return php code can only be done via eval() and that it is somewhat of an unsafe code as it leaves you open to php injections. Thank you #revo
I have transitioned over and learned how to code a template engine and used a .tpl file so that users cannot put their own php code into the page and everything gets processed using a php page running in the background using functions (template engine). This prevented me from having to use an eval() code as #revo recommended that I stick away from using this without proper validation (which I'm not sure I want to even have to worry about the validation to be honest).
I wanted to inform everyone who viewed this question of what I learned and would suggest working with template engines & functions and avoid using the method I am recommending in my question.
Thank you #revo for working with me on a possible solution and keeping me informed of vulnerabilities.

Does strip_tags() handle embedded tags

I'm away from a computer with PHP installed and was wondering what the result of strip_tags() would be on the following text:
"<scr<h1>ipt>alert('oh oh')</scr</h1>ipt>"
Would it return:
"<script>alert('oh oh')</script>" (i.e. not recognize that by removing the obvious tag it exposed a new one)
or
"alert('oh oh')
I know that if it returns the first case I can just repeatedly call the function until I get out what I put in, but I'm curious.
Thanks in advance.
Great question.
Nope, it doesn't strip anything from that string:
<?php
$b = "ipt>alert('oh oh')ipt>";
echo strip_tags($b);
?>
And the output is your original string: ipt>alert('oh oh')ipt>
Edit
In your second case it will print alert('oh oh') so it strips all that is looking like a tag in a single step
It returns just:
alert('oh oh')

Look for function calls in a bunch of templates by using a script

I have a load of php templates which uses a custom translate function "__", i.e.
<?php echo __("Hello"); ?>
I need to write a script which will look for all these function calls (there are about 200 templates).
I.e. it will find __("Hello") and add it to a sentences to be translated array. For example it will find:
$sentences[] = "Hello";
$sentences[] = "Goodbye";
$sentences[] = "Random sentence to be translated";
Basically i need to find the strings which need to be translated.
Which do you think is the best language for doing the script in? and do you think it will be best to use a regular expression?
Any help to point me in the right direction would be superb!
Thanks
I always jump to Perl for string manipulation problems.
However, awk or sed could easily solve your problem.
For example, in Perl:
while(<>) {
if( $_ =~ /echo __\((".*?")\)/ ) {
print '$sentences[] = ' + $1;
}
}
Note, this will only capture one string per line. You can do more, but I'll leave that as an exercise to the reader.
Also, the 'while(<>)' will loop through each line in each file you pass on the command line. There's also a way read all of the files in a directory if that's what you need.

Errors with basic PHP template using eval()

I'm just about ready to cry. I have read the php.net manual page, tried a dozen Google searches with various phrases, and scanned hundreds of stackoverflow threads. I have seen this method in use, but it just doesn't work for me. It seems so basic. I have a couple related questions or problems I don't know how to find answers to.
The important part of the PHP file looks like this:
switch() {
…other cases…
default:
$tpl['title'] = "Newsletter Signup";
$tpl['description'] = "Newsletter description";
$tpl['page-content'] = file_get_contents('signup.html');
}
$tpl_src = addslashes(file_get_contents('index.tpl'));
eval("\$html = \"$tpl_src\";");
echo $html;
My index.tpl file includes lines like these:
<title>{$tpl['title']}</title>
<meta name="description" content="{$tpl['description']}" />
nav, etc…
<div id="main-content"> {$tpl['page-content']} </div>
I like how neat and clean the code is, without a whole bunch of extra <?=…?>'s.
First, when curly brackets {} appear in a string, what is that called? I might be able to look it up and learn how to use them if I knew.
Next, this just doesn't work at all. If I remove the single quotes from the variable keys, it's good, but php.net says you should never do that in case my name becomes a language constant at some point. Fair enough. But how do I fix this? I like using an array for the vars in case I want to build an evalTemplate subroutine and can just pass $tpl to it.
Lastly, $tpl['page-content'] doesn't print out. The variable is set okay; I can use echo $tpl['page-content'] to test, but it appears as a single blank line in the final HTML.
I'm sure there's just some aspect of the language I don't know yet. Any help is much appreciated!!
As Volker pointed out, addslashes seems to be an issue. Try addcslashes instead. Also, I'd strongly recommend making this a function, to simplify sanitisation / parsing:
function render ($file, $vars)
{
// .. extra sanitisation, validation, et al.
$_html = '';
$_raw_file = addcslashes (file_get_contents ($file), '"\\');
extract ($vars, EXTR_SKIP);
eval ('$_html = "'.$_raw_file.'"');
return $_html;
}
And called thus:
switch() {
// …other cases…
default:
$tpl['title'] = "Newsletter Signup";
$tpl['description'] = "Newsletter description";
$tpl['page-content'] = render ('signup.html');
}
echo render ('index.tpl', $tpl);
PS: The use of extract above means that your vars will simply be $title, not $tpl['title'], etc.
Usually you don't use the '' string delimiters in string variable expansion. I.e. "$tpl[content]" instead of "$tpl['content']".
As for as the braces, they delimit variable's when identifier characters may come straight before or after the name. For example:
$item = "Cup";
$text = "I smashed four $items"; // won't work
$text = "I smashed four {$item}s"; // will work.
// 2nd output: "I smashed four Cups"
addslashes() adds slashes before both single- and double-quotes within the string.
The code generated for your example would be
$html = "<title>{$tpl[\'title\']}</title>
<meta name=\"description\" content=\"{$tpl[\'description\']}\" />
nav, etc…¦
<div id=\"main-content\"> {$tpl[\'page-content\']} </div>";
And {$tpl[\'title\']} doesn't parse well.

how to eval() a segment of a string

I have a string that has HTML & PHP in it, when I pull the string from the database, it is echo'd to screen, but the PHP code doesn't display. The string looks like this:
$string = 'Hello <?php echo 'World';?>';
echo $string;
Output
Hello
Source Code
Hello <?php echo 'World';?>
When I look in the source code, I can see the php line there. So what I need to do is eval() just the php segment that is in the string.
One thing to consider is that the PHP could be located anywhere in the string at any given time.
* Just to clarify, my PHP config is correct, this is a case of some PHP being dumped from the database and not rendering, because I am echo'ing a variable with the PHP code in it, it fails to run. *
Thanks again for any help I may receive.
$str = "Hello
<?php echo 'World';?>";
$matches = array();
preg_match('/<\?php (.+) \?>/x', $str, $matches);
eval($matches[1]);
This will work, but like others have and will suggest, this is a terrible idea. Your application architecture should never revolve around storing code in the database.
Most simply, if you have pages that always need to display strings, store those strings in the database, not code to produce them. Real world data is more complicated than this, but must always be properly modelled in the database.
Edit: Would need adapting with preg_replace_callback to remove the source/interpolate correctly.
You shouldn't eval the php code, just run it. It's need to be php interpreter installed, and apache+php properly configured. Then this .php file should output Hello World.
Answer to the edit:
Use preg_replace_callback to get the php part, eval it, replace the input to the output, then echo it.
But. If you should eval things come from database, i'm almost sure, it's a design error.
eval() should work fine, as long as the code is proper PHP and ends with a semicolon. How about you strip off the php tag first, then eval it.
The following example was tested and works:
<?php
$db_result = "<?php echo 'World';?>";
$stripped_code = str_replace('?>', '', str_replace('<?php', '', $db_result));
eval($stripped_code);
?>
Just make sure that whatever you retrieve from the db has been properly sanitized first, since you're essentially allowing anyone who can get content into the db, to execute code.

Categories