Effectively Theming long stringed dynamic html - php

I'm making several HTML popups that always follows a defined template.
Since there is a template (header,content,example table,more button),
I thought I can save a lot of recurring html by passing the data to a wrapper function like so:
$device_popup_data = array(
'header' => 'Header text',
'content' => 'some text<span style ="bold"> Some more text</span>',
'example' => '<table><tbody><tr><td> A lot of text here, multi lined and messy',
'more' => '',
);
echo theme_uxt_expanded_popup($device_popup_data);
function theme_uxt_expanded_popup($data){
$head = isset($data['head'])?$data['head']:'';
$content = isset($data['content'])?$data['content']:'';
$example = isset($data['example'])?$data['example']:'';
$more_html = isset($data['more'])?$data['more']:'';
$output= '<div class = "expandedTooltip">';
$output.= '<h1>'.$head.'</h1>';
$output.= '<p>'.$content.'</p>';
if(!empty($more)){
$output.= '<a class = "popupShowMore"><p>'.$more.'</p></a>';
}
$output .= '</div>';
return $output;
}
This seemed like a great idea, until I have seen that some of these fields, like the example field, may contain around 100 lines of HTML.
Pushing these long strings into the example variable seems to make very unreadable code. Something like this :
$device_popup_data = array(
'header' => 'Header text',
'content' => 'some text<span style ="bold"> Some more text</span>',
'example' => '<table><tbody><tr><td> A lot of text here</td>,<td> multi lined and
messy, and if any one</td>
<td>wants to change this string it will be very hard</td>
Real string is much longer ... </table>',
'more' => '',
);
Do you know of an efficient and readable way of doing something like this?

Do you know of an efficient and readable way of doing something like
this?
The only readable, maintainable way of doing this is to adhere to the Separation of Concerns. The points here are 1) Decoupling HTML from PHP 2) Implementing a container, like name => HTML content
You should really wrap that into a class, in order to take full advantage of DI and SRP
(see below). So, a class itself would look like as:
class TemplateBlockManager
{
private $blocks = array();
public function define($name, $file, array $vars = array())
{
ob_start();
if (!empty($vars)) {
extract($vars);
}
require($file);
$content = ob_get_clean();
$this->blocks[$name] = $content;
}
public function getBlock($name)
{
return $this->blocks[$name];
}
}
File : test.phtml
<p>
<b>Welcome to <?php echo $foo; ?></b>
</p>
Usage:
<?php
$blockManager = new TemplateBlockManager();
$blockManager->define('header', '/path/to/test.phtml', array('foo' => 'bar'));
// Should output "Welcome to bar"
echo $blockManager->getBlock('header');
This approach has a number of advantages:
You can prepare several blocks at a bootstrap stage, thus your blocks can be shared
across your pages. This reduces code duplication
You can inject an instance of a $blockManager to another classes that generate an output. This is good for unit-testing, as it adheres to the Dependency Injection
You also adhere to the Single-Responsibility Principle
You totally decouple HTML from PHP, since your templates contain basic (or none) php logic
Since your templates are totally decoupled you don't have to worry if they are long or small. You would simply define a path to the one
And finally, both HTML and PHP code are easy to maintain

Related

Multilingual PHP Inline Replacement in HTML ([en]...[/en] etc.)

I am a coding beginner and have my PHP/HTML web project in German. Now I want to make it available in English in the easiest way. I don't want to add other languages in the future, so I want to try it in the easiest (and maybe not the most proper) way.
I have PHP files with HTML content and the selected language available in a var, i.e.:
<?php
$lang = "en";
?>
<h1>Title in German</h1>
So all the German words are inline HTML. My idea was to create something like:
<h1>[de]Title in German[/de][en]Title in English[/en]</h1>
But I have no idea how to replace it on every load in a smart way. So it is more a topic on "live replacement".
Working with constants in an external language file is of course also an option, like all the other options to make a multilingual site I found on Stackoverflow.
But maybe there is a "quick and dirty" possibility option like the one I mentioned?
Thank you for every hint!
You could try and do this will almost only HTML and CSS. You would need to add this at the top of your page:
<?php
$pageLanguage = "en";
function getLanguageStyle($showLanguage)
{
global $pageLanguage;
$display = ($showLanguage == $pageLanguage ? 'inline' : 'none');
return " span.$showLanguage { display: $display }\n";
}
echo "<style>\n".
getLanguageStyle('en').
getLanguageStyle('de').
"</style>\n";
?>
It sets up a style for each language, which you can then use like this:
<h1><span class="de">Title in German</span><span class="en">Title in English</span></h1>
The advantage here is that you don't need to mix HTML and PHP. This is not a normal way of doing this, but it will work. On very complex pages, where these styles are applied after the first render, this might not be pleasant for your visitors.
Usually translations are made that way:
You have key to translation map for each language, then you request some function that takes proper map for that language and returns translation:
function translate(string $lang, string $key) {
/*
* This usually sits in some file in dir like `/src/i18n/en.json`
* And you do then `$translations = json_decode(require "/src/i18n/{$lang}.json")`
*/
$translations = [
'en' => [
'page.title' => 'Page Title',
...
],
'de' => [
'page.title' => 'Page Title In German',
...
],
];
return $translations[$lang][$key] ?? $key;
}
<h1><?= translate($lang, 'page.title'); ?></h1>

Unable to put php code in return of shortcode

I need to hide the URLs of downloads in wordpress posts. I have found a great script to do this but it is not a plugin. I have installed the script and created a function to include it. I am not a pro with php at all.
However the script has a line to normally call it:
<a href="<?php downloadurl('http://yourdomainname.comdownloadables.zip','veryspecials'); ?>" >Your Downloadables</a>
I am not able to place this directly in posts so I am trying to make a shortcode for it, but I am getting stuck. The shortcode that I have is:
function secshort_func($atts, $content = null) {
extract(shortcode_atts(array(
"linkurl" => '#Download_Does_Not_Exist',
"linktitle" => 'Download',
), $atts));
return '<a href="<?php downloadurl(' .$linkurl. ','veryspecials'); ?>" >' .$linktitle. '</a>';
}
add_shortcode( 'secdown', 'secshort_func' );
I am getting errors when trying to run this, and through a process of elimination I know that it is from this part of the return code:
"<?php downloadurl(' .$linkurl. ','veryspecials'); ?>"
After searching the internet for solutions and trying everything that I can think of, I am completely stuck.
Any help would be very much appreciated - it is driving me crazy being stuck on such a little thing!
A few observations along with the answer:
Format your code. It makes life much easier when troubleshooting. Good indenting is huge (see below for your code, formatted).
Don't use cryptic / abbreviated function names. Type them out so that you create self documenting code.
It's better to not use extract. There are some who say it's OK, but it can create confusing code that is hard to troubleshoot because you don't know where a variable came from. It's preferable to explicitly set the variables. (In your case, because you use them only once, it's simplest to just reference them in the array form - $atts['link_url'])
You can call the function, but it has to be concatenated into the string (see below). Your code (and the other answer), are passing php into the string, rather than calling the function and passing the result into the string.
Formatted code, with answer:
// Use a clearer function name. No need for "func", that's implied
function download_link_shortcode($atts, $content = NULL) {
// Declare $defaults in a separate variable to be clear, easy to read
$defaults = array(
"link_url" => '#Download_Does_Not_Exist',
"link_title" => 'Download',
);
// Merge the shortcode attributes
$atts = shortcode_atts( $defaults, $atts );
// Concatenate in the results of the `download` function call....
return '' . $atts['link_title'] . '';
}
add_shortcode( 'secdown', 'download_link_shortcode' );
Try using double outer quotes and escape inner single quotes like this:
return "<a href=\'<?php downloadurl(\'" . $linkurl . "\',\'veryspecials\'); ?>\' >" .$linktitle. '</a>';

Routing, Navigation and State in MVC

I am attempting to refactor my app using the MVC paradigm.
My site displays charts. The URLs are of the form
app.com/category1/chart1
app.com/category1/chart2
app.com/category2/chart1
app.com/category2/chart2
I am using Apache Rewrite to route all requests to index.php, and so am doing my URL parsing in PHP.
I am working on the enduring task of adding an active class to my navigation links when a certain page is selected. Specifically, I have both category-level navigation, and chart-level sub-navigation. My question is, what is the best way to do this while staying in the spirit of MVC?
Before my refactoring, since the nav was getting relatively complicated, I decided to put it into an array:
$nav = array(
'25th_monitoring' => array(
'title' => '25th Monitoring',
'charts' => array(
'month_over_month' => array(
'default' => 'month_over_month?who=total&deal=loan&prev='.date('MY', strtotime('-1 month')).'&cur='.date('MY'),
'title' => 'Month over Month'),
'cdu_tracker' => array(
'default' => 'cdu_tracker',
'title' => 'CDU Tracker')
)
),
'internet_connectivity' => array(
'title' => 'Internet Connectivity',
'default' => 'calc_end_to_end',
'charts' => array(
'calc_end_to_end' => array(
'default' => 'calc_end_to_end',
'title' => 'calc End to End'),
'quickcontent_requests' => array(
'default' => 'quickcontent_requests',
'title' => 'Quickcontent Requests')
)
)
);
Again, I need to know both the current category and current chart being accessed. My main nav was
<nav>
<ul>
<?php foreach ($nav as $category => $category_details): ?>
<li class='<?php echo ($current_category == $category) ? null : 'active'; ?>'>
<?php echo $category_details['title']; ?>
</li>
<?php endforeach; ?>
</ul>
</nav>
and the sub-nav was something similar, checking for current_chart instead of current_category.
Before, during parsing, I was exploding $_SERVER['REQUEST_URI'] by /, and breaking the pieces up into $current_category and $current_chart. I was doing this in index.php. Now, I feel this is not in the spirit of the font controller. From references like Symfony 2's docs, it seems like each route should have its own controller. But then, I find myself having to define the current category & chart multiple times, either within the template files themselves (which doesn't seem to be in the spirit of MVC), or in an arbitrary function in the model (which would then have to be called by multiple controllers, which is seemingly redundant).
What is the best practice here?
Update: Here's what my front controller looks like:
// index.php
<?php
// Load libraries
require_once 'model.php';
require_once 'controllers.php';
// Route the request
$uri = str_replace('?'.$_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI']);
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && (!empty($_GET)) && $_GET['action'] == 'get_data') {
$function = $_GET['chart'] . "_data";
$dataJSON = call_user_func($function);
header('Content-type: application/json');
echo $dataJSON;
} elseif ( $uri == '/' ) {
index_action();
} elseif ( $uri == '/25th_monitoring/month_over_month' ) {
month_over_month_action();
} elseif ( $uri == '/25th_monitoring/cdu_tracker' ) {
cdu_tracker_action();
} elseif ( $uri == '/internet_connectivity/intexcalc_end_to_end' ) {
intexcalc_end_to_end_action();
} elseif ( $uri == '/internet_connectivity/quickcontent_requests' ) {
quickcontent_requests_action();
} else {
header('Status: 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
?>
It seems like when month_over_month_action() is called, for instance, since the controller knows the current_chart is month_over_month, it should just pass that along. This is where I'm getting tripped up.
There are not "best practices" in this area. Though, there are some, that are more often used then others, and some, that are extremely bad ideas (unfortunately, these two groups tend to overlap).
Routing in MVC
While technically not a part of MVC design pattern, when applied to Web, your application needs to know which controller to initialize and what method(s) to call on it.
Doing explode() to gather this sort of information is a bad idea. It is both hard to debug and maintain. A much better solution is to use regular expressions.
Basically you end up having a list of routes, that contain a regular expression and some fallback values. You loop through that list and on fists match extract the data and apply default values, where data was missing.
This approach also frees you to have much wider possibilities for order of parameters.
To make the solution easier to use, you can also add functionality, that turns a notation string into a regular expression.
For example (taken from some unit-test, that I have):
notation:     test[/:id]
expression: #^/test(:?/(?P<id>[^/\.,;?\n]+))?$#
notation:     [[/:minor]/:major]
expression: #^(:?(:?/(?P<minor>[^/\.,;?\n]+))?/(?P<major>[^/\.,;?\n]+))?$#
notation:     user/:id/:nickname
expression: #^/user/(?P<id>[^/\.,;?\n]+)/(?P<nickname>[^/\.,;?\n]+)$#
While creating such a generator will not be all that easy, it would be quite reusable. IMHO the time invested in making it would be well spent. Also, the use of (?P<key>expression) construct in regular expressions provides you with a very useful array of key-value pairs from the matched route.
Menus and MVC
The decision about which menu item to highlight as active should always be the responsibility of current view instance.
More complicated issue is where the information, that is necessary for making such decision, comes from. There are two source if data that are available to a view instance: information that was passed to view by controller and data, that view requested from model layer.
The controller in MVC takes the user's input and, based on this input, it changes the state of current view and model layer, by passing said values. Controller should not be extracting information from model layer.
IMHO, the better approach in this case is to relay on model layer for information about both menu content and the currently active element in it. While it's possible to both hardcode the currently active element in view and relay on controllers passed informations, MVC is usually used in large scale application, where such practices would end up hurting you.
The view in MVC design pattern is not a dumb template. It's a structure, that is responsible for UI logic. In context of Web that would mean creating a response from multiple template, when necessary, or sometimes just simply sending an HTTP location header.
Well, I had almost the same trouble when was writing CMS-like product.
So I've spend some time trying to figure out how to make this work and keep the code more maintainable and clean as well.
Both CakePHP and Symfony route-mecanisms have a bit inspired me but it wasn't good enough for me.
So I'll try to give you an example of how I do this now.
My question is, what is the best way to do this while staying in the
spirit of MVC?
First, In general, best practice is NOT TO USE procedural approach with MVC in web development at all.
Second, keep the SRP.
From references like Symfony 2's docs, it seems like each route should
have its own controller.
Yeah, that's right approach, but it doesn't mean that another route match can't have the same controller, but different action.
The main disadvantage of your approach (code that you have posted) is that you mix responsibilities and you're not implementing MVC-inspired pattern.
Anyway, MVC in PHP with procedural approach is just a horrible thing.
So, what exactly you are mixing is:
Route mechanism logic (It should be another class) not in a "controller" and route map as well
Request and Response responsibilites (I see that it isn't obvious to you)
Class autoloading
Controller logic
All those "parts" should have one class. Basically, they have to be included in index or bootstrap files.
Also, by doing so:
require_once 'controllers.php';
You automatically include ALL controllers per match (even on no-match). It actually has nothing to do with MVC and leads to memory leaks.
Instead, you should ONLY include and instantiate the controller that matches against URI string.
Also, be careful with include() and require() as they may lead to code duplication if you include the same file somewhere twice.
And also,
} elseif ( $uri == '/' ) {
index_action();
} elseif ( $uri == '/25th_monitoring/month_over_month' ) {
month_over_month_action();
} elseif ( $uri == '/25th_monitoring/cdu_tracker' ) {
cdu_tracker_action();
} elseif ( $uri == '/internet_connectivity/intexcalc_end_to_end' ) {
intexcalc_end_to_end_action();
It's extremely unwise to do a match using if/else/elseif control structures.
Okay, what if you have 50 matches? or even 100? Then you need to write 50 or 100 times to write else/elseif accordingly.
Instead, you should have a map and (an array for example) iterate over it on each HTTP request.
The general approach of using MVC with routing mechanism comes down to:
Matching the request against route map (and keep somewhere parameters if we have them)
Then instantiate appropriate controller
Then pass parameters if we have them
In PHP, the implementation would look like:
File: index.php
<?php
//.....
// -> Load classes here via SPL autoloader or smth like this
// .......
// Then -> define or (better include route map from config dir)
$routes = array(
// -> This should default one
'/' => array('controller' => 'Path_To_home_Controller', 'action' => 'indexAction'),
'/user/:id' => array('controller' => 'Path_to_user_controller', 'action' => 'ViewAction'),
// -> Define the same controller
'/user/:id/edit' => array('controller' => 'Path_to_user_controller', 'action' => 'editAction'),
// -> This match we are going to hanlde in example below:
'/article/:id/:user' => array('controller' => 'SomeArticleController', 'action' => )
);
// -> Also, note you can differently handle this: array('controller' => 'SomeArticleController', 'action' => )
// -> Generally controller key should point to the path of a matched controller, and action should be a method of the controller instance
// -> But if you're still on your own, you can define it the way you want.
// -> Then instantiate common classes
$request = new Request();
$response = new Response();
$router = new Router();
$router->setMap( $routes );
// -> getURI() should return $_SERVER['REQUEST_URI']
$router->setURI( $request->getURI() );
if ( $router->match() !== FALSE ) {
// -> So, let's assume that URI was: '/article/1/foo'
$info = $router->getAll();
print_r ( $info );
/**
* Array( 'parameters' => Array(':id' => '1', ':user' => 'foo'))
* 'controller' => 'Path_To_Controller.php'
* 'action' => 'indexAction'
*/
// -> The next things we are going to do are:
// -> 1. Instantiate the controller
// -> 2. Pass those parameters we got to the indexAction method
$controller = $info['controller'];
// -> Assume that the name of the controller is User_Controller
require ( $controller );
// -> The name of class should also be dynamic, not like this, thats just an example
$controller = new User_Controller();
$arguments = array_values( $info['parameters'] );
call_user_func_array( array($controller, $info['action']), $arguments );
// -> i.e we just called $controller->indexAction('1', 'foo') "dynamically" according to the matched URI string
// -> idealy this should be done like: $response->send( $content ), however
} else {
// -> In order not to show any error
// -> redirect back to "default" controller
$request->redirect('/');
}
In my MVC-inspired applications I do route like this:
(Where I use Dependecy Injection and keep the SRP)
<?php
require (__DIR__ . '/core/System/Auload/Autoloader.php');
Autoloader::boot(); // one method includes all required classes
$map = require(__DIR__ . '/core/System/Route/map.php');
$request = new Request();
$response = new Response();
$mvc = new MVC();
$mvc->setMap( array_values($map) );
// -> array_values($map) isn't accurate here, it'd be a map of controllers
// -> take this as a quick example
$router = new Router();
$router->setMap( $map );
$router->setURI( $request()->getURI() );
if ( $router->match() !== FALSE ) {
// -> Internally, it would automatically find both model and view instances
// -> then do instantiate and invoke appropriate action
$router->run( $mvc );
} else {
// No matches handle here
$request->redirect('/');
}
I found this to be more appropriate for me, after poking around Cake and Symfony.
One thing I want to note:
It's not that easy to find good articles about MVC in PHP. Most of them are just wrong.
(I know how it feels, because first time I've started to learn from them, like so many people do)
So my point here is:
Don't make the same mistake like I did before. If you want to learn MVC, start doing this by reading
Zend Framework or Symfony Tutorials. Even the ones are bit different, the idea behing the scene is the same.
Back to the another part of the question
Again, I need to know both the current category and current chart
being accessed. My main nav was
<nav>
<ul>
<?php foreach($nav as $category => $category_details): ?>
<li class='<?php echo ($current_category == $category) ? null : 'active'; ?>'>
<?php echo $category_details['title']; ?>
</li>
<?php endforeach; ?>
</ul>
</nav>
First of all, don't concatenate the string, instead use printf() like:
<?php echo $category_details['title']; ?>
If you need this to be everywhere (or at least in many different templates), I'd suggest to this to have in a common abstact View class.
For example,
abstract class View
{
// -> bunch of view reusable methods here...
// -> Including this one
final protected function getCategories()
{
return array(
//....
);
}
}
class Customers_View extends View
{
public function render()
{
$categories =& $this->getCategories();
// -> include HTML template and then interate over $categories
}
}

PHP delay the output of headers until the entire PHP script has been run?

It is possible to delay the output of the headers to the browser until the entire php script has finished executing?
The headers will be sent once the first piece of code is sent from php to the webserver.
So you can just "not echo anything" until the end of the script or use output buffering to achieve the same thing
This is the way most professional programmers would advise, the reasons you should always do it this way is that you can manage errors and error pages effectively.
If your application is already built to display output as the script is executed then i would advise you to start from scratch.
the way I usually manage output is with a small template's system, the template's system does not have to parse templates etc, it just needs to be passed a set of data and then include the required template.
you should create a class that acccepts data in the form of $template->set(key[,value = true]) and then a function that would display such as $template->display(filename), when this function executes you should extract the variables and then include the template file, after that has been done you then call exit(0) so no further code is executed.
a simple template system can be like so:
class Template
{
private $_data = array();
public function set($key,$value = true)
{
$this->_data[$key] = $value;
}
public function display($template)
{
//Check template exuists
extract($this->_data);
require_once $template;
exit(0);
}
}
then use pretty simply like so:
$template = new Template();
$template->set("title","Practical home Page");
$template->set("header","My Home Page");
$lists = array(
array(
"value" => "list item 1",
"id" => "list_item_1",
"class" => "item"
),
array(
"value" => "list item 2",
"id" => "list_item_2",
"class" => "item"
)
);
$template->set("menu",$lists);
$template->display("templates/homepage.php");
you may also would like to read the following answer I had answered previously!
PHP Looping Template Engine - From Scratch

What is the best way to handle recursion in smarty?

I found a couple of ways to handle recursion in Smarty, mostly based on including templates into themselves, which seems like ridiculous waste of resources. I found one solution, by Messju over at Smarty that seemed to be just right - but it is not supported and fails in the latest version of smarty :(
For people asking: What I want smarty to print out is a discussion thread that is defined by an array of entries. If an entry has one or more answers, those are listed as children to said entry in an array, and so on.
array(
array(
'id'=>0,
'headline'=>"My parent headline",
'body' =>"My parent body",
'children'=>array(
array(
'id'=>1,
'headline'=>"My firstChild headline",
'body' =>"My firstChild body",
'children'=>array()
),
array(
'id'=>2,
'headline'=>"My secondChild headline",
'body' =>"My secondChild body",
'children'=>array()
)
)
),
);
The nested array has an arbitrary depth, and each entry will have an arbitrary number of children. To me this is something I want to do with within the scope of the template, as I consider it pure display logic. I do not want to have to handle HTML or some type of HTML placeholders outside of the template.
I want smarty to print this as nested lists:
<ul>
<li>
<h1>My parent headline</h1>
<p>My parent body</p>
<ul>
<li>
<h1>My firstChild headline</h1>
<p>My firstChild body</p>
</li>
<li>
<h1>My secondChild headline</h1>
<p>My secondChild body</p>
</li>
</ul>
</li>
</ul>
I'm starting to realize this might be a very case-by-case problem, so I figure I'll just write a smarty plugin to handle this specifically, although I'd rather have an all-around solution.
Is there a way?
With Smarty 3, this can be done using {function}. The following code will produce the required ouput.
{function name=printList}
<ul>
{foreach $items as $item}
<li>
<h1>{$item['headline']}</h1>
<p>{$item['body']}</p>
{if $item['children']}
{call name=printList items=$item['children']}
{/if}
</li>
{/foreach}
</ul>
{/function}
{call name=printList items=$comments}
More information can be found at the docs.
Side note: Just because something is complex or recursive it doesn't mean that it can't be inside a template. For God's sake the HTML ul-li structure is naturally recursive and by hiding it away or moving it somewhere else (just because it is too complex for a template) you are introducing an extra complexity into the application.
"In order to understand recursion, you must first understand recursion..."
Just kidding. This should do what you want:
<?php
/*
* Smarty plugin
* ————————————————————-
* File: function.recurse_array.php
* Type: function
* Name: recurse_array
* Purpose: prints out elements of an array recursively
* ————————————————————-
*/
function smarty_function_recurse_array($params, &$smarty)
{
if (is_array($params['array']) && count($params['array']) > 0) {
$markup = '';
$markup .= '<ul>';
foreach ($params['array'] as $element) {
$markup .= '<li>';
$markup .= '<h1>' . $element['headline'] . '</h1>';
$markup .= '<p>' . $element['body'] . '</p>';
if (isset($element['children'])) {
$markup .= smarty_function_recurse_array(array('array' => $element['children']), $smarty);
}
$markup .= '</li>';
}
$markup.= '</ul>';
return $markup;
} else {
return 'not array';
}
}
Place the file into your smarty/plugins folder. Assign your array to Smarty then call it in your template like so:
{recurse_array array=$data}
Here's nice tutorial for making custom Smarty functions:
Creating Custom Smarty Functions
Be aware of the dependency that this example has on your underlying data structure. Also, keep in mind that an unusually long or deeply nested set of data could be really slow. Manage your complexity, keep things well documented, and you should be fine. Good luck!
You might want to consider creating custom function/modifier/plugin for smarty.
Pass the array to the custom function along with defining what is the template the function should use. If it is that simple, only to insert a text to certain place, load the template within function and in PHP work with the template using regexes/str_replace/...
Or do it directly in PHP without using smarty templates, because all you need is h1, ul, li and p tags and to change the layout use CSS.
Or if your concern is the overhead with opening and closing files in Smarty, estimate what is the amount of levels in 90% of cases and create template which will cover those 90%. For the rest use recursion by including the template itself...
The best way is not to do it.
Smarty is supposed to be simple. This deesn't sound it.

Categories