19

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?

4

7 Answers 7

45

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

Sign up to request clarification or add additional context in comments.

8 Comments

If you want to protect the included file from variable pollution (namely $this) you can wrap it in a rebound closure: call_user_func(Closure::bind(function () { include func_get_arg(0); }, null ), $path);
Maybe I am wrong, but as far as I understand this example you are just overwriting the variable $newsletter, which you have declared before, with the same value. Since you stil have to echo the variable afterwards the example seems invalid to me and is equivalent to $x = 'foo'; echo $x;
@LeonKramer no, $newsletter will not get overwritten. In the template file it's just echo'd out. It works because $newsletter outside and inside the template are in the same scope. Variable scope
Okay, got it now. This short class is very sweet!
This is beautiful, something I've wanted to know for a long time! Thanks for this.
|
17

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);
      }
    }

2 Comments

Did you mean sprintf('$%s', $$varname) ?
Actually it looks like it should be sprintf('%s', $$varname)
9

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;

Comments

6

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;
?>

Comments

2

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...'));

1 Comment

if(file_exists($file)){ makes it very, very bad. One should never suppress errors.
1
<?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.

Comments

-1

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));

13 Comments

Using eval is very insecure and not allowed in some installations.
Actually, I was just reading the docs; the default return value for an include statement is 1, or false if the include failed (I'm not sure why it's not boolean true, but that's what the docs say).
@Gustav it's as secure as including a file in this context. You aren't acting on user input. (and eval can not be disabled with vanilla php, it requires an extra extension...)
@Gustav he is using eval to... evaluate a script he loaded externally. There is no user input involved and the script would have been just as eval'd in an include. These use cases are the reason eval exists in the first place. There is nothing insecure about running your own code. When people tell you eval is bad this is not what they mean at all.
@IMSoP in his example it was no user input, but a raw string from the file…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.