I am attempting to make a (very) basic template engine for php. Based on my research I have found that a method that I am using is strongly disliked. I was wondering if anyone knew a great alternative to get the same result so I am not using it. And if anyone sees any other improvements that can be made please share!
the method that is not advised is the eval() method!
Here is the php file
<?php
class Engine {
private $vars = array();
public function assign($key, $value) {
$this->vars[$key] = $value;
}
public function render($file_name) {
$path = $file_name . '.html';
if (file_exists($path)) {
$content = file_get_contents($path);
foreach ($this->vars as $key => $value) {
$content = preg_replace('/\{' . $key . '\}/', $value, $content);
}
eval(' ?>' . $content . '<?php ');
} else {
exit('<h4>Engine Error</h4>');
}
}
}
?>
here is the index.php file
<?php
include_once 'engine.php';
$engine = new Engine;
$engine->assign('username', 'Zach');
$engine->assign('age', 21);
$engine->render('test');
?>
and here is just a test html file to display its basic function
My name is {username} and I am {age} years old!
outputs:
My name is Zach and I am 21 years old!
Many thanks in advance!
If you just want to output some text to the page, just use echo:
echo $content;
This is better than eval('?>' . $content . '<?php') for quite a few reasons: for one, if someone types in <?php phpinfo(); ?>, for example, as their username, it won't execute that code.
I would, however, note that you have some other problems. What if I do this?
$engine = new Engine;
$engine->assign('username', '{age}');
$engine->assign('age', 21);
$engine->render('test');
The {age} in the username value will be replaced with 21. Usually you don't want replacements to be replaced like that, particularly as it's order-dependent (if you assigned username later, it wouldn't happen).
Related
I wanna replace braces with <?php ?> in a file with php extension.
I have a class as a library and in this class I have three function like these:
function replace_left_delimeter($buffer)
{
return($this->replace_right_delimeter(str_replace("{", "<?php echo $", $buffer)));
}
function replace_right_delimeter($buffer)
{
return(str_replace("}", "; ?> ", $buffer));
}
function parser($view,$data)
{
ob_start(array($this,"replace_left_delimeter"));
include APP_DIR.DS.'view'.DS.$view.'.php';
ob_end_flush();
}
and I have a view file with php extension like this:
{tmp} tmpstr
in output I save just tmpstr and in source code in browser I get
<?php echo $tmp; ?>
tmpstr
In include file <? shown as <!--? and be comment. Why?
What you're trying to do here won't work. The replacements carried out by the output buffering callback occur after PHP code has already been parsed and executed. Introducing new PHP code tags at this stage won't cause them to be executed.
You will need to instead preprocess the PHP source file before evaluating it, e.g.
$tp = file_get_contents(APP_DIR.DS.'view'.DS.$view.'.php');
$tp = str_replace("{", "<?php echo \$", $tp);
$tp = str_replace("}", "; ?>", $tp);
eval($tp);
However, I'd strongly recommend using an existing template engine; this approach will be inefficient and limited. You might want to give Twig a shot, for instance.
do this:
function parser($view,$data)
{
$data=array("data"=>$data);
$template=file_get_contents(APP_DIR.DS.'view'.DS.$view.'.php');
$replace = array();
foreach ($data as $key => $value) {
#if $data is array...
$replace = array_merge(
$replace,array("{".$key."}"=>$value)
);
}
$template=strtr($template,$replace);
echo $template;
}
and ignore other two functions.
How does this work:
process.php:
<?php
$contents = file_get_contents('php://stdin');
$contents = preg_replace('/\{([a-zA-Z_][a-zA-Z_0-9]*)\}/', '<?php echo $\1; ?>', $contents);
echo $contents;
bash script:
process.php < my_file.php
Note that the above works by doing a one-off search and replace. You can easily modify the script if you want to do this on the fly.
Note also, that modifying PHP code from within PHP code is a bad idea. Self-modifying code can lead to hard-to-find bugs, and is often associated with malicious software. If you explain what you are trying to achieve - your purpose - you might get a better response.
Run into a bit of a sticky situation which I can't seem to wrap my finger around. Basically what I am trying to achieve is having the ability to inject different Javascript files on different page.
Some simple, random example:
Page 1: import jquery.js
Page 2: import mootools.js
So what I have done is, I've created a function called addScript() like so:
function addScript($file) {
$script = '';
$script .= '<script src="'. REL_PATH . '/path/to/file/' . $file . '">';
$script .= '</script>';
return $script;
}
so if I call addScript('jquery.min'); it, outputs correctly.
What I now want to do is replace the closing </head> tag with the output from the above function. If I do the following then it works fine:
ob_start();
require_once("models/header.php");
$contents = ob_get_contents();
ob_end_clean();
echo str_replace('</head>', addScript('jquery.js') . '</head>', $contents);
However I would like this to be a little more dynamic as there may be multiple script that I need to inject on each page like so:
addScript('script.js');
addScript('script2.js');
addScript('script3.js');
I then thought of creating a getHead() function with a foreach loop inside and returning str_replace there instead but this did not work.
Can anyone guide my in the direction to dynamically inject as many script as required and output the last bit of the head?
Why not do something like this:
class Assets {
private static $css = array();
private static $js = array();
static function add_style($path) {
self::$css[] = $path;
}
static function add_script($path) {
self::$js[] = $path;
}
static function get_styles() {
$output = '';
foreach(self::$css as $path) {
$ouput .= '<link rel="stylesheet" href="'. $path .'" />' . "\n";
}
return $ouput;
}
static function get_scripts() {
$output = '';
foreach(self::$js as $path) {
$ouput .= '<script type="text/javascript" src="'. $path .'"></script>' . "\n";
}
return $ouput;
}
}
Then anywhere in your project:
Assets::add_style('path/to/style.css');
Assets::add_script('path/to/jquery.js');
And in header.php:
<head>
<!-- other header stuff -->
<?php echo Assets::get_styles(); ?>
<?php echo Assets::get_scripts(); ?>
</head>
Is much more convenient, and you can can extend the class to do more fancy stuff.
Disclaimer: there is much debate about using static vars, as they look like globals. I agree, but this is quick-and-dirty and works no matter what kind of framework you use. You can also make the variables oldschool instance vars, but then you'll have to pass the assets object to the header.php as well.
What's wrong with the following??
echo str_replace('</head>',
addScript('jquery.js').
addScript('jquer1.js').
addScript('jquer2.js').
addScript('jquer3.js').
'</head>', $contents);
How about you put the ob_start(); in header.php. Then your function is:
function addScript($file) {
$script = '<script src="'. REL_PATH . '/path/to/file/' . $file . '"></script>';
echo str_replace('</head>', addScript('jquery.js') . '</head>', ob_get_clean());
}
Then:
addScript('script.js');
This method keeps the output buffer going and you can manipulate it later in the script whenever you want. just as you do with the addScript().
I'm working on a new project. I route everything to router.php, but I have a problem now. I'm using keywords, so I can use:
<[DEFAULT_JS]>
And the return is:
<script src="./assets/js/jquery-1.11.0.min.js" type="text/javascript"></script>
This is the code:
// some code
if(in_array($_GET['p'], $allowedPages)) {
$source = file_get_contents('./pages/'.$allowedPages[$_GET['p']]);
foreach($keywords as $key => $value) {
$source = str_replace("<[".$key."]>", $value, $source);
}
echo $source;
} else {
// some code
You can see, I'm using file_get_contents to get the script and replace the 'keywords', but now it won`t send GET data with the page. When I use include it should work but then I cannot use the 'keywords' anymore.
So, how can include a file and replace the keywords?
So basically you are inventing some template system and you want to use PHP in your templates?
I think that you can use eval() function for that. But it's still not a good option.
// some code
if(in_array($_GET['p'], $allowedPages)) {
$source = file_get_contents('./pages/'.$allowedPages[$_GET['p']]);
foreach($keywords as $key => $value) {
$source = str_replace("<[".$key."]>", $value, $source);
}
eval($source);
} else {
// some code
I'm including a file in one of my class methods, and in that file has html + php code. I return a string in that code. I explicitly wrote {{newsletter}} and then in my method I did the following:
$contactStr = include 'templates/contact.php';
$contactStr = str_replace("{{newsletter}}",$newsletterStr,$contactStr);
However, it's not replacing the string. The only reason I'm doing this is because when I try to pass the variable to the included file it doesn't seem to recognize it.
$newsletterStr = 'some value';
$contactStr = include 'templates/contact.php';
So, how do I implement the string replacement method?
You can use PHP as template engine. No need for {{newsletter}} constructs.
Say you output a variable $newsletter in your template file.
// templates/contact.php
<?= htmlspecialchars($newsletter, ENT_QUOTES); ?>
To replace the variables do the following:
$newsletter = 'Your content to replace';
ob_start();
include('templates/contact.php');
$contactStr = ob_get_clean();
echo $contactStr;
// $newsletter should be replaces by `Your content to replace`
In this way you can build your own template engine.
class Template
{
protected $_file;
protected $_data = array();
public function __construct($file = null)
{
$this->_file = $file;
}
public function set($key, $value)
{
$this->_data[$key] = $value;
return $this;
}
public function render()
{
extract($this->_data);
ob_start();
include($this->_file);
return ob_get_clean();
}
}
// use it
$template = new Template('templates/contact.php');
$template->set('newsletter', 'Your content to replace');
echo $template->render();
The best thing about it: You can use conditional statements and loops (full PHP) in your template right away.
Use this for better readability: https://www.php.net/manual/en/control-structures.alternative-syntax.php
This is a code i'm using for templating, should do the trick
if (preg_match_all("/{{(.*?)}}/", $template, $m)) {
foreach ($m[1] as $i => $varname) {
$template = str_replace($m[0][$i], sprintf('%s', $varname), $template);
}
}
maybe a bit late, but I was looking something like this.
The problem is that include does not return the file content, and easier solution could be to use file_get_contents function.
$template = file_get_contents('test.html', FILE_USE_INCLUDE_PATH);
$page = str_replace("{{nombre}}","Alvaro",$template);
echo $page;
based on #da-hype
<?php
$template = "hello {{name}} world! {{abc}}\n";
$data = ['name' => 'php', 'abc' => 'asodhausdhasudh'];
if (preg_match_all("/{{(.*?)}}/", $template, $m)) {
foreach ($m[1] as $i => $varname) {
$template = str_replace($m[0][$i], sprintf('%s', $data[$varname]), $template);
}
}
echo $template;
?>
Use output_buffers together with PHP-variables. It's far more secure, compatible and reusable.
function template($file, $vars=array()) {
if(file_exists($file)){
// Make variables from the array easily accessible in the view
extract($vars);
// Start collecting output in a buffer
ob_start();
require($file);
// Get the contents of the buffer
$applied_template = ob_get_contents();
// Flush the buffer
ob_end_clean();
return $applied_template;
}
}
$final_newsletter = template('letter.php', array('newsletter'=>'The letter...'));
<?php
//First, define in the template/body the same field names coming from your data source:
$body = "{{greeting}}, {{name}}! Are You {{age}} years old?";
//So fetch the data at the source (here we will create some data to simulate a data source)
$data_source['name'] = 'Philip';
$data_source['age'] = 35;
$data_source['greeting'] = 'hello';
//Replace with field name
foreach ($data_source as $field => $value) {
//$body = str_replace("{{" . $field . "}}", $value, $body);
$body = str_replace("{{{$field}}}", $value, $body);
}
echo $body; //hello, Philip! Are You 35 years old?
Note - An alternative way to do the substitution is to use the commented syntax.
But why does using the three square brackets work?
By default the square brackets allow you to insert a variable inside a string.
As in:
$name = 'James';
echo "His name is {$name}";
So when you use three square brackets around your variable, the innermost square bracket is dedicated to the interpolation of the variables, to display their values:
This {{{$field}}} turns into this {{field}}
Finally the replacement with str_replace function works for two square brackets.
no, don't include for this. include is executing php code. and it's return value is the value the included file returns - or if there is no return: 1.
What you want is file_get_contents():
// Here it is safe to use eval(), but it IS NOT a good practice.
$contactStr = file_get_contents('templates/contact.php');
eval(str_replace("{{newsletter}}", $newsletterStr, $contactStr));
I have a problem with 3rd-party-system integration in my drupal site.
Sorry for my english, i'm from russia, but i will try to explain my problem well.
Integration idea:
2 .php files
2 php-script lines (include
function's)
The problem is:
this scripts call to outside perl
(.pl) script. Perl script read the
parameters (parameters transfers by
url) and generate content.
I can't see this perl script, but i
know - hes working, but not in my
page :)
2 php files:
spectrum_view.php
<?php
$url = "http://young.spectrum.ru/cgi-bin/programs_view.pl";
$param = $_GET;
if (!empty($param))
{
$url .= "?";
foreach ($param as $keys=>$value)
{
$url .= "&".$keys."=".urlencode($value);
}
} echo $content = file_get_contents($url);
?>
spectrum_form.php
<?php
$url ="http://young.spectrum.ru/cgi-bin/programs_form.pl";
$params = $_GET;
if (!empty($params))
{
$url .= "?";
foreach ($params as $keys=>$value)
{
$url .= "&".$keys."=".urlencode($value);
}
} echo iconv("windows-1251","utf-8",(file_get_contents($url)));
?>
and the 2 php-lines, wich i insert in my drupal pages
(the first i insert in page http://new.velo-travel.ru/view
and the second in the right block)
include("http://new.velo-travel.ru/themes/themex/spectrum_view.php?$QUERY_STRING");
include("http://new.velo-travel.ru/themes/themex/spectrum_form.php?act=/view$QUERY_STRING");
So, i solved this problem, but not in drupal - only on my Localohost, i just create a 2 page:
form.php:
<?php
$url ="http://young.spectrum.ru/cgi-bin/programs_form.pl";
$params = $_GET;
if (!empty($params)){
$url .= "?";
foreach ($params as $keys=>$value) $url .= "&".$keys."=".urlencode($value);
}
$content = file_get_contents($url);
print $content;
**require_once 'view.php';**
?>
view.php:
<?php
$url = "http://young.spectrum.ru/cgi-bin/programs_view.pl";
$param = $_GET;
if (!empty($param))
{
$url .= "?";
foreach ($param as $keys=>$value)
{
$url .= "&".$keys."=".urlencode($value);
}
}
$content = file_get_contents($url);
print $content;
?>
=(
I'm not entirely sure, as to what you are trying to do. But it seems like you want to generate this content from the perl script. If this is a special page with it's own template, you should move all this code into template.php. This file is made to hold some logic you want to create the content for your page.
Personally I would prefer to make a module to handle all this, but it's probably easier to do this in the theme, with what you got now. It seems like you are making a form, and some content based on the form. This could be done in a module. You could create a Drupal form, and then handle the validation with drupal, and jst submit the data to perl. But if you would want to get it from perl, going with the theme is probably best. So how do you do it?
Implement a preprocess function for the tpl.php file you use.
Create all the logic here, you could copy the php files you use over or just include them. Import, assign the result to a variable the will be accessible in the template file.
Print the variable in your template.
In code this would look something like this:
//template.php file
function mytheme_preprocess_somename(&$vars) {
include('php');
// Do some logic.
$vars['form'] = $result_a;
$vars['my_content'] = $result_b;
}
// your .tpl.php
// Some markup here
<div><?php print $my_content; ?></div>
<div><?php print $form; ?></div>
Now, I'm not sure exactly what you are after, but something like this should help you along. Note it's important what you call your variables inside the template file, as you can overwrite some Drupal variables like $content, which can cause some bugs.
You probably are running into a security issue. Please note allow_url_fopen and allow_url_include - these settings must have accordant settings in your php.ini. Otherwise you can't e.g. include a remote file for security reasons.