views:

663

answers:

6

Hi,

I am wondering - What's the most effective way of parsing something like:

{{HEADER}}

Hello my name is {{NAME}}

{{#CONTENT}}
    This is the content ...

    {{#PERSONS}}

        <p>My name is {{NAME}}.</p>

    {{/PERSONS}}

{{/CONTENT}}

{{FOOTER}}

Of course this is intended to be somewhat of a templating system in the end, so my plan is to create a hashmap to "lay over" the template, as something like this

$hash = array(
    'HEADER' => 'This is a header',
    'NAME' => 'David',
    'CONTENT' => array('PERSONS' => array(array('NAME' => 'Heino'), array('NAME' => 'Sebastian')),
    'FOOTER' => 'This is the footer'
    );

It's worth noticing that the "sections" (the tags that start with #), can be repeated more than once, and i think this is what trips me up ...

Also, any section can contain any number of other sections, and regular tags...

So.. how'd you do it?

A: 

You would bet better off using something with an existing parser like XML or JSON so you don't have to write your own parser, and so that others can easily write documents for your parser without needing specialized tools. However, if you want to write your own parser, you probably want to look into using Lex and Yacc.

Kibbee
A: 

I would go with a third party parser because I like to work smarter and not harder, but if you're doing this as an exercise or you really want to build your own template engine (in PHP I assume because of the tag), I would start with reviewing design patterns, the composite design pattern specifically.

The composite pattern is used a lot in the Java framework for stuff like this including XML parsing.

Jesse Dearing
+1  A: 

Is the expected output of this something like:

This is a header

Hello my name is David

This is the content ...

My name is Heino.

My name is Sebastian.

This is the footer


How are you managing the relationship of nested arrays in the hash map to repeatable sections in the template? What is the actual behaviour of the template supposed to be? If an array is provided for a non-section element, what will it do? If a section element is provided a single value, will it be treated the same as an array with only a single element (I assume so)?

Anyhow, with regards to the parser for the template (regardless of what you end up doing with the mapping of data)... What I would do is create a class for each type of token, including a generic one for non-token content. These would inherit from a common token base class with overridable Parse, Render and Map methods.

Chart out your state diagram and figure out what your entry and exit points are for each state, then encode that into the call structure between the tokens. In the end you want to yield an enumerable collection of tokens that describes your template.

Once you have that in an abstract form, you can iteate over the collection calling Map on the tokens to assign the data from the hashmap to the tokens, and then call Render to render the template into it's final form.

Hope that helps.

Troy Howard
If nothing "fits" for a tag, the tag should be ignored and stripped away. Otherwise i like your idea - although it may be a bit more complex that i originally thought it would be.
David
A: 

I would use something like this inside a .php separate file:

<?php echo $HEADER ?>

Hello my name is <?php echo $NAME?>

<div id="CONTENT">
    This is the content ...

    <?php foreach ($PERSONS as $PERSON) : ?>

        <p>My name is <?php echo $PERSON['NAME']?>.</p>

    <?php endforeach ?>

</div>

<?php echo $FOOTER ?>

And just include the above file inside the one where the referenced variables are populated.

Believe it or not, PHP already provides all the features that templating systems out there are claiming to implement. There is no need to add another layer of abstraction (and complexity) on top of PHP.

Yes, that would be the simple thing to do, but it's not suffice in the situation i'm in. No PHP/logic can be embedded in the templates at all.
David
I'm assuming that your request of not having any PHP logic comes from the fact that the templates will be generated by non-programmers. But I'm sure that if you can teach them to write {{#PERSONS}} when an iteration for each person is needed you could also teach them to write the foreach equivalent.
A: 

I use PHP's DOM for this. My template language is simply HTML with ID and class attributes. If you want to stick with your plan, though, I'd use preg_replace_callback with a pattern that matches your syntax and a callback function that finds the appropriate replacement in your hash, calling itself recursively on container elements.

Scott Reynen
This is exactly what i'm fiddling with right now. But was wondering if theres other approaches to parsing such a structure.
David
+1  A: 

The most efficient way to is to compile the template to php code. And just include the compiled version.

The Smarty Template Engine does something similar. You can also look at the smarty source and check how they parse tags.

Bob Fanger