Say I have a string "Some text [login_form] some other text". How can I replace '[login_form]' with the PHP code "require('somescript.php');" and run the 'require' function.
I don't want to use 'eval' as my string contain HTML and other code and also has great possibility of errors.
You can do this:
search with regex for [\[(.*)\]]
$replace = include_once($matched_string);
replace [\[(.*)\]] with $replace
Hmm?
Maybe this could also work:
$string = preg_replace_callback(
'/\[(.*)\]/',
function($matches) {
ob_start();
include $matches[1].'.php';
return ob_get_clean();
},
$string
);
EDIT: I saw this approach within a few "home made" CMS but instead of includeing files they all called a class or function. It could be extended with parameters like Hey, check out this new gallery: [gallery, 15, 200, 200].
You parse that string and find out that You have to call object $gallery, probably a method view with the parameters 15, 200, 200 that will be how many images to show per page and the image thumbnail resolution... So You will call $gallery->view(15, 200, 200);.
In this case the above PHP code will be extended to:
$string = preg_replace_callback(
'/\[(.*)\]/',
function($matches) {
$params = explode(', ', $matches[1]); // by this we get an array with object name and all the parameters
$object = array_shift($params);
return ${$object}->view($params); // for simplicity we pass parameters as an array
},
$string
);
Is this what You want to achieve?
I think eval was your only option here. If you so not want to use it for some reasons (which probably are good reasons), you are stuck. Maybe you can give some more context so we can give you other options?
As long as somescript.php is properly formatted and has a closing tag if an open php tag exists, eval will work, although I am not all for eval.
eval('?>'.file_get_contents('somescript.php').'<?');
Can you clarify a little on what you mean by replace [login_form] with a code example you have?
Related
I have a script with a regex, which detect all defined internal functions in php. The script is working well, but there is a problem with the linebreaks.
With this script I searching for internal and defined funtions from PHP. Every function have to be replaced with the new function "no_function()".
$functions=get_defined_functions();
for($i=0;$i<count($functions["internal"]);$i++){
//Include array functions
if($functions["internal"][$i]=="array"||$functions["internal"][$i]=="array_push"){
}else{
$code=preg_replace('#('.$functions["internal"][$i].'[ |\n|\r|\t]*\([ |\n|\r|\t]*.*\))#i',"no_function()",$code);
}
}
When I had a function like:
str_replace($search,$replace,$string);
The function will not called because it will replace with no_function();
It will work normally. But if I have a function like that:
str_replace($search,
$replace,
$string);
The regex don't detect that, but PHP will execute that as well. I am a noob with regex. Can someone help me?
Thanks for every response.
Not sure about your use case. But you could use namespaces and redeclare internal functions under your namespace for a quick hack that will replace internal functions with no return.
First make a file consisting of dummy internal functions.
<?php
$funcs = get_defined_functions();
$output = "<?php\n";
$output .= "namespace example;";
foreach ($funcs['internal'] as $func_name) {
$output.= "function $func_name () {}\n";
}
file_put_contents('dummy_funcs.php', $output);
Then in another file (perhaps a common bootstrap) include your dummy functions early.
<?php
namespace example;
include 'dummy_funcs.php';
echo str_replace('w', 'l', 'bawls');
Outputs nothing.
The upshot here is that you don't need to edit your code (other than adding the include). It could however be a pain depending on your existing namespacing.
There is a known way to include a file and capture its contents into a string while loading.
$string = get_include_contents('somefile.php');
function get_include_contents($filename) {
if (is_file($filename)) {
ob_start();
include $filename;
return ob_get_clean();
}
return false;
}
https://www.php.net/manual/en/function.include.php
Is there a way to "include" contents loading them from a string instead of a file?
I mean something like this:
$string = file_get_contents("file.php");
include_from_string($string);
If you want the string to be parsed as PHP code, just like the contents of a file loaded with include(), then the function you need is eval().
Note that, unlike code loaded by include(), code executed by eval() automatically starts in PHP mode, so you don't need to (and shouldn't!) prefix it with <?php. If you want to emulate the behavior of include() exactly, you can prefix the string to be eval()ed with ?> to leave PHP mode:
$string = file_get_contents( 'somefile.php' );
eval( '?>' . $string );
Also note that eval() is a very dangerous function to play with! While in this specific case it shouldn't be any more risky than include() itself is, using eval() on any string that might even possibly contain unsanitized (or insufficiently sanitized) user input is extremely dangerous, and may be exploited by attackers to execute malicious code on your system and thereby gain control of it.
This might not be what you are looking for but I got "work around" for it.
Just create temporary file with tempnam() which you will include and then unlink().
$path = "somefile.php";
$stringFile = file_get_contents($path);
$pathTmp = tempnam("tmp/", ""); // you pass directory in which you will store tmp files for me it's "tmp/"
$file = fopen($pathTmp, "w+");
fwrite($file,$widget);
fclose($file);
include $pathTmp; // include the file, and PHP will be automatically parsed
unlink($pathTmp); // delete file
THIS IS WRONG:
I'm not sure if it's good practice (but hack damn, it's simple) because no one suggested it but it's better then eval() which is basically "code hazard".
THIS IS RIGHT:
As #Chris Harrison commented this is security risk and it's equal to eval(). So you could basically do this:
eval($string);
This is a simple example for you, if you pass inside the eval() this will execute the code in the string variable.
<?php
//here your PHP Code goes
$string = get_include_contents('somefile.php');
//evaluating the string this will work
eval($string); //output
This is not equivalent to using include. Here's the problem: eval() takes the provided PHP, and executes it in the current environment. Thus, any globals, functions, classes, what-not, you have defined prior to the eval() are available for the processor. This is all good, and, upon return, the only thing left of the original (evel'd) string are the results of any echo (or equivalent) statements.
This is NOT the same as an include. There the file contents are merged with your source code and that is passed to eval(). Very, very different. The easiest way to see this is to define your string as 'class fu { static function bar() { echo "wow"; } ]' Put this in a file and call fu::bar() and you'll get 'wow' displayed. At the same point in your code, if you do an eval('class fu ...') and call fu::bar() from your code you'll get "Fatal error: Call to private method fu::bar() from context ..."
But, as long as you don't need to interact with the 'include' the results will appear the same.
Just echo whatever you want instead of include inside your function!
UPDATE
Your function should look like this:
$string = "Whatever";
$str = get_var($string);
function get_var($str) {
ob_start();
echo $str;
return ob_get_clean();
}
Problem
I'd like to expand variables in a string in the same manner that variable in a double quoted string get expanded.
$string = '<p>It took $replace s</>';
$replace = 40;
expression_i_look_for;
$string should become '<p>It took 40 s</>';
I see a obvious solution like this:
$string = str_replace('"', '\"', $string);
eval('$string = "$string";');
But I really don't like it, because eval() is insecure. Is there any other way to do this ?
Context
I'm building a simple templateing engine, that's where I need this.
Example Template (view_file.php)
<h1>$title</h1>
<p>$content</p>
Template rendering (simplified code):
$params = array('title' => ...);
function render($view_file, $params)
extract($params)
ob_start();
include($view_file);
$text = ob_get_contents();
ob_end_clean();
expression_i_look_for; // this will expand the variables in the template
return $text;
}
The expansion of the variables in the template simplifies it's syntax. Without it, the above example template would be:
<h1><?php echo $title;?></h1>
<p><?php echo $content;?></p>
Do you think this approach is good ? Or should I look in another direction ?
Edit
Finally I understand that there is no simple solution due to flexible way PHP expands variables (even ${$var}->member[0] would be valid.
So there are only two options:
Adopt an existing full fledged templating system
Stick with something very basic that essentially is limited to including the view files via include.
I would rather suggest using some existing template engines, like for example Smarty, but if you really want to do it by yourself you can use the simple regular expression to match all variables constructed with for example letters and numbers and then replace them with correct variables:
<?php
$text = 'hello $world, what is the $matter? I like $world!';
preg_match_all('/\$([a-zA-Z0-9]+)/',
$text,
$out, PREG_PATTERN_ORDER);
$world = 'World';
$matter = 'matter';
foreach(array_unique($out[1]) as $variable){
$text=str_replace('$'.$variable, $$variable, $text);
}
echo $text;
?>
prints
hello World, what is the matter? I like World!
Parse
Parse the string look for $ followed by valid variable name (i.e. \[a-zA-Z_\x7f-\xff\]\[a-zA-Z0-9_\x7f-\xff\]*)
Variable²
Use variable variables syntax (i.e. $$var notation).
Are you trying to do this?
templater.php:
<?php
$first = "first";
$second = "second";
$third = "third";
include('template.php');
template.php:
<?php
echo 'The '.$first.', '.$second.', and '.$third.' variables in a string!';
When templater.php is run, produces:
"The first, second, and third variables in a string!"
Do you want something like this ?
$replace = 40;
$string = '<p>It took {$replace}s</p>';
Instead of using single quotes
$string = '<p>It took $replace s</>';
$replace = 40;
use double quotes
$replace = 40;
$string = "<p>It took $replace s</>";
However, for readability and to enable you to remove the space between $replace and the s I would use:
$replace = 40;
string = '<p>It took ' . $replace . 's</>';
The correct way is probably to parse your document as a tree, identify your parser tags ( because you are managing your own parser they don't have to follow php conventions if you don't want them to ) and then add in your values from an associative array or other data structure as the opportunity arises.
This is a more complex solution but will make it far easier when you realise that you want to be able to display lists whose length is unknown ahead of time using some kind of looping structure based on a standard display option. In the long run, you won't find many serious templating systems that aren't parsing the documents into some kind of in-memory tree where the placeholders can be inserted and then the document constructed as required. This also offers many opportunities for cacheing. Also, if you are unafraid of recursion you will be able to perform a lot of operations on it fairly simply.
However, this is not an uncommon problem to solve and as I commented on the question, there are almost guaranteed to be libraries and extensions around that provide most of the functionality you need. Unless this is a purely academic process for you, I would find some existing solutions and either use one of those or get a solid understanding of how it works so you have a starting point for adapting your own solution.
This is a snippet I pulled out from Lejlot's answer. I tested it and it works fine.
function resolve_vars_in_str( $input )
{
preg_match_all('/\$([a-zA-Z0-9]+)/', $input, $out, PREG_PATTERN_ORDER);
foreach(array_unique($out[1]) as $variable) $input=str_replace('$'.$variable, $GLOBALS["$variable"], $input);
return $input ;
}
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
The routine below does two scans over an input stream of hypertext. The first pass is a spin replacement on user defined phrase options. The second pass is a find replace on the tags collection in the doReplace function below.
I'm just looking for suggestions on how it might be optimized. I'm having no performance issues as is. But I want to build for scalability.
/* FIND REPLACE SPIN
--------------------------------------------------------------------*/
function doReplace($content)
{
// content is a precompiled text document formatted with html and
// special using replacement tags matching the $tags array collection below
$tags = array('[blog-name]', '[blog-url]', '[blog-email]');
$replacements = array('value1', 'value2', 'value3');
$content = str_replace($tags, $replacements, $content);
return $content;
}
function doSpin($content) {
// the content also has phrase option tags denoted by [%phrase1|phrase2_|phrase3%]
// delimiters throughout the text.
return preg_replace_callback('!\[%(.*?)%\]!', 'pick_one', $content);
}
function pick_one($matches) {
$choices = explode('|', $matches[1]);
return $choices[rand(0, count($choices)-1)];
}
$my_source_page = file_get_contents('path/to/source';}
$my_source1_spin = doSpin($my_source_page);
$my_source1_replace = doReplace($my_source1_spin);
$my_source1_final = addslashes($my_source1_replace);
//Now do something with $my_source1_final
To be honest, I don't see anything wrong with the code you've posted. The main bottleneck in the code is likely going to be the file_get_contents call.
The only thing I can see myself is that you're allocating the string to different variables (four variables beginning $my_source) which will use more memory than if you just used 1 or 2 variables.
But unless you're reading a large amount of text into memory very frequently on a busy site, then I don't think you need to worry about the code you've posted. And you said yourself, you're not having any performance issues at the moment ;)