4

I'm trying to build a CMS in PHP and having a little trouble with building a template system. I don't want to use Smarty or Twig because I don't want to rely on external frameworks until I can fluently code myself. It is all about enhancing my learning (from my perspective).

So I have been planning out how the templating will work but have run into trouble with the error checking.

Here is a basic overview of how it works.

(very) simple sample template:

<html>
<p>{output $randomNumber}</p>{output $databaseDump}
<div>{output $databaseAndUsersPasswords}</div>
</html>

Gets parsed by my parser:

<html>
<p><?php echo $randomNumber?></p><?php echo $databaseDump?>
<div><?php echo $databaseAndUsersPasswords?></div>
</html>

Then I use eval to run the parsed template.

Where my issue lies is in Error checking. If a designer were to get something wrong there is very little protection against an error. With error suppression enabled a portion of the page will just be missing its content. I want it to be all or nothing. I can't think of a simple solution that will allow me to check for errors.

Also, do you think I am okay using regular expression and str_replace to parse the template? I have been reading over some other frameworks solutions, but it all looks terribly over complicated, I can't locate the core of their parsing system.

Please criticize my techniques all you like. I am here to learn.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Sam
  • 1,564
  • 4
  • 23
  • 37
  • If you're generating PHP code .. you could just use `include` :) – Ja͢ck Dec 28 '12 at 08:03
  • Whatever you do don't you ever use `eval` to parse anything. Is there a reason you cannot / doesn't want to just use plain PHP as the templating engine? – PeeHaa Dec 28 '12 at 08:06
  • @HankyPanky could you please elaborate? Do you want an example of an error?@PeeHaa it might be a bit pedantic, but I was concerned about the braces "{}" used in php constructs and if they would just confuse the designer. Also, how would I cleanly output a multidimensional array as a formatted table without causing the designer confusion? You do however post a reasonable arguement, one that I been considering, but am unsure about. I realize it would be much faster. I just look at the likes of PHPBB, it looks more pro to me. In the long run neither will make a difference to the end user. – Sam Dec 28 '12 at 08:37

2 Answers2

4

If you want all or nothing, you could simply configure an error handler and let it throw exceptions:

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    if (error_reporting()) {
        // the @ operator wasn't used, throw it.
        throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
    }
}
set_error_handler("exception_error_handler");

After this, you can include() your generated script inside a try { } catch block and use output buffering to prevent any unwanted output:

try {
    ob_start(); // stop output

    include '/path/to/script';

    ob_end_flush(); // flush buffer and stop output buffering
} catch (ErrorException $e) {
    ob_end_clean(); // clear buffer and stop output buffering
}

See also: ErrorException


Regarding writing your own templating engine, I use this rule:

If it looks like PHP, use PHP!

A syntax that almost resembles PHP is going to be just as difficult to learn for designers than PHP itself, but the effort you spend making sure the templates doesn't trip up your own parser is definitely tangible.

If you want something that weaves better into what designers work with, check out this project called PHPTAL which uses XHTML attributes to do variable substitution and declare looping blocks, macros, etc.


More on error reporting and logging: Error logging, in a smooth way

Community
  • 1
  • 1
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309
  • Thanks for that. I was looking at using output buffering on eval (if possible), but it seems that eval is out of the question now. Also, I didn't see any benefits to saving a file and then immediately loading, except for a cache of course. I'll have a good read of the error logging post of yours. Thanks! – Sam Dec 28 '12 at 08:39
  • @Sam Compiling a template each time can be more taxing than you think ;-) in some cases, many components get loaded in the process that would otherwise not be necessary once compiled. You also benefit from opcode caching (APC) if you write intermediate files. – Ja͢ck Dec 28 '12 at 08:53
  • 1
    If it looks like PHP, use PHP!++ – PeeHaa Dec 28 '12 at 09:03
  • +1 Great answer full of useful resources. BTW, this answer should have more upvotes, IMHO. – Leri Dec 28 '12 at 09:28
1

First of all, don't use eval (read the caution) unless it's necessary and you don't have another choice. Another thing is that for templating system it's overkill to take view, convert to php and then include (run) it.

I would use MVC pattern and let controller load view and pass necessary information to it without processing one as a script.

Sample:


Base controller:

abstract class Controller {
    protected function Render($view, $data = array()) {
        $viewStr = file_get_contents(__PATH_TO_VIEW__.$view.'.html');

        $viewStr = preg_replace_callback('/\{(\w+)\}/',
                                        function ($matches) use ($data) {
                                            return isset($data[$matches[1]]) ?
                                                          $data[$matches[1]] : '';
                                        }, $viewStr);
        echo $viewStr;
    }
}

Custom controller:

class MyController extends Controller {
    public function index() {
        $this->Render('myView', array('test' => 'some test string'));
    }
}

Custom view (myView.html in this example):

<b>{test}</b>

Output should be:

some test string


As you see, you are working with string, you don't run anything, so now you have full control over view and designer is not able to produce php error.

P.S. Feel free to adapt this ample to your existing code. ;)

Leri
  • 12,367
  • 7
  • 43
  • 60
  • Hi, thanks for that. I am actually trying to incorporate all of this into an MVC application, but perhaps I am approaching it wrong. What you have done there is similar to what I used to do, assuming I am reading it correctly. Where the template contains placeholders and you use a model to render it. You have a fantastic solution, but I just wonder about what happens with things such as arrays of data. Do I use a little bit of HTML within my PHP or something? Basically, prebuild the table contents then swap it with the placeholder? – Sam Dec 28 '12 at 08:29
  • @Sam Actually controller renders data in placeholder. So your script won't process designer's work, but render it. For more and better explanations take a look at [these answers](http://stackoverflow.com/users/727208/teresko?tab=answers&sort=votes). – Leri Dec 28 '12 at 08:33
  • @Sam Let's say I have 10 posts that I need to render. Template of each post is the same, so I'll load this template for each item, than pass it to my main template that has something like: `{posts}`. This is first solution that came in my mind. It maybe improved. – Leri Dec 28 '12 at 08:35
  • Thankyou. This is all very interesting. I found this particularly good: http://stackoverflow.com/questions/5863870/how-should-a-model-be-structured-in-mvc/5864000#5864000 . I have a lot of reading to do now. Thanks. I am still a little confused as to how the rendering takes place. I'll take another look. – Sam Dec 28 '12 at 08:43
  • This is how I am understanding your version and basically how I used to do this: The template contains placeholders. The script imports the source of the template. The script then uses regular expression to take out the placeholder and replace it with data supplied in an array. The template is then echoed. Am I correct? – Sam Dec 28 '12 at 08:47