I am designing my own MVC pattern to ease the process of creating homepages. My templating system needs my controller class to output my views. This means I have to output the file through a php function. I have been searching for some a while now and can't seem to find a solution.
How can I, through a PHP function, run a string representing some source code ("< ?", "< ?php", "? >" and so on) as php? Eval would not take my < ? signs (and I read that function is crap for some reason).
You could execute the php code and collect the output like this:
ob_start();
include "template.phtml";
$out1 = ob_get_clean();
http://www.php.net/manual/de/function.ob-get-contents.php
Just include()ing the file instead should be fine. I've not dug that deeply into the source code but I'm fairly sure that's how Zend Framework implements templates.
Replacing "echo file_get_contents()" with "include()" as GordonM suggested worked exactly as needed. can't upvote yet since I'm too new but for my needs this is the direct answer to the headlined question.
$code = '<?php
function GetBetween($content, $start, $end) {
$r = explode($start, $content);
if (isset($r[1])){
$r = explode($end, $r[1]);
return $r[0];
}
return '';
}';
function _readphp_eval($code) {
ob_start();
print eval('?>'. $code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
print _readphp_eval($code);
Well, eval is not crap per se, it's just easy to create a security hole with that if you're not careful about what you allow inside that eval (that, and maybe the fact that it can be bad for readability of the code).
As for the <? signs, that's because eval expects php code, so if you wan't to mix php with plain output, include just the closing ?> tag.
In general however, you can implement templates in better fashion, try to look into output buffering.
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 get the html from another site with file_get_contens, my question is how can i get a specific tag value?
let's say i have:
<div id="global"><p class="paragraph">1800</p></div>
how can i get paragraph's value? thanks
If the example is really that trivial you could just use a regular expression. For generic HTML parsing though, PHP has DOM support:
$dom = new domDocument();
$dom->loadHTML("<div id=\"global\"><p class=\"paragraph\">1800</p></div>");
echo $dom->getElementsByTagName('p')->item(0)->nodeValue;
You need to parse the HTML. There are several ways to do this, including using PHP's XML parsing functions.
However, if it is just a simple value (as you asked above) I would use the following simple code:
// your content
$contents='<div id="global"><p class="paragraph">1800</p></div>';
// define start and end position
$start='<div id="global"><p class="paragraph">';
$end='</p></div>';
// find the stuff
$contents=substr($contents,strpos($contents,$start)+strlen($start));
$contents=substr($contents,0,strpos($contents,$end));
// write output
echo $contents;
Best of luck!
Christian Sciberras
(tested and works)
$input = '<div id="global"><p class="paragraph">1800</p></div>';
$output = strip_tags($input);
preg_match_all('#paragraph">(.*?)<#is', $input, $output);
print_r($output);
Untested.
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.