Templating Engine

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

d3ad1ysp0rk
Forum Donator
Posts: 1661
Joined: Mon Oct 20, 2003 8:31 pm
Location: Maine, USA

Templating Engine

Post by d3ad1ysp0rk »

Ok, so, I've been hired/volunteered to redo a site's coding from scratch. Right now it works fine, but when you look at the code, it's html/php/javascript all jumbled together in huge, unnecessary files (about 400 at last count).
First thing I'm thinking of is the templating engine. I've been bouncing ideas around, but I have no one to bounce them off but myself, and I just keep trying to find a better way, and I can't really think of it.

Here's a few file ideas:
1.inc (template 1):

Code: Select all

<html>
<head>
<title><?php echo $title; ?></title>
<link rel="stylesheet" type="text/css" href="incs/style.css" />
<script language="javascript" src="<?php echo $js; ?>"></script>
</head>
<body>
<div id="main_container">
   <div id="header"><?php echo $header; ?></div>
   <div id="left">
      <div id="nav"><?php echo $nav; ?></div>
      <div id="ads"><?php echo $ads; ?></div>
   </div>
   <div id="middle">
      <div id="main_contents"><?php echo $contents; ?></div>
      <div id="top5"><?php echo $top5; ?></div>
   </div>
   <div id="right">
      <div id="stats"><?php echo $stats; ?></div>
   </div>
</div>
</body>
</html>
index.php:

Code: Select all

<?php include("functions.php");
if(empty($_GET['p'])) $p = "home";
$html = new Page();
$html->build($p);
$html->print();
?>
functions.php:

Code: Select all

<?php class Page {
  var nav;
  var ads;
  function build($p){
    include("tpls/" .$user[tpl]. ".inc");
    eval($page); //would be in Page->print();
    //this is where I'm stuck, this code won't work.. because it doesn't evaluate the php AND parse the html.. :\ am i better to just do a str_replace of [ads], [nav], etc, and then echo it out?
  }
  function vars(){
    $result = mysql_query("SELECT * FROM site_links");
    while($row = mysql_fetch_array($result)){
      $this->nav .= "<a href="" .$row[link]. "">" .$row[name]. "</a><br />";
    }
    $this->ads = include("incs/ads.txt");
  }
}
?>

Ok, so can anyone give pointers for where to go next with this, how to improve it, how to print out the page, opinions on the method im using, etc..?

Thanks a lot.
By the way, I'm not in a rush, so please try to give me as much information as you can about this, thanks :)
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

I think phpBB's template system works pretty well..
page header (subSilver) template:
source: overall_header.tpl

Code: Select all

<?php ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html dir="{S_CONTENT_DIRECTION}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset={S_CONTENT_ENCODING}">
<meta http-equiv="Content-Style-Type" content="text/css">
{META}
{NAV_LINKS}
<title>{SITENAME} :: {PAGE_TITLE}</title>
<!-- link rel="stylesheet" href="templates/subSilver/{T_HEAD_STYLESHEET}" type="text/css" -->
<style type="text/css">
<!--
/*
  The original subSilver Theme for phpBB version 2+
  Created by subBlue design
  http://www.subBlue.com

  NOTE: These CSS definitions are stored within the main page body so that you can use the phpBB2
  theme administration centre. When you have finalised your style you could cut the final CSS code
  and place it in an external file, deleting this section to save bandwidth.
*/

/* General page style. The scroll bar colours only visible in IE5.5+ */
body { 
	background-color: {T_BODY_BGCOLOR};
	scrollbar-face-color: {T_TR_COLOR2};
	scrollbar-highlight-color: {T_TD_COLOR2};
	scrollbar-shadow-color: {T_TR_COLOR2};
	scrollbar-3dlight-color: {T_TR_COLOR3};
	scrollbar-arrow-color:  {T_BODY_LINK};
	scrollbar-track-color: {T_TR_COLOR1};
	scrollbar-darkshadow-color: {T_TH_COLOR1};
}

/* General font families for common tags */
font,th,td,p { font-family: {T_FONTFACE1} }
a:link,a:active,a:visited { color : {T_BODY_LINK}; }
a:hover		{ text-decoration: underline; color : {T_BODY_HLINK}; }
hr	{ height: 0px; border: solid {T_TR_COLOR3} 0px; border-top-width: 1px;}

/* This is the border line & background colour round the entire page */
.bodyline	{ background-color: {T_TD_COLOR2}; border: 1px {T_TH_COLOR1} solid; }

/* This is the outline round the main forum tables */
.forumline	{ background-color: {T_TD_COLOR2}; border: 2px {T_TH_COLOR2} solid; }

/* Main table cell colours and backgrounds */
td.row1	{ background-color: {T_TR_COLOR1}; }
td.row2	{ background-color: {T_TR_COLOR2}; }
td.row3	{ background-color: {T_TR_COLOR3}; }

/*
  This is for the table cell above the Topics, Post & Last posts on the index.php page
  By default this is the fading out gradiated silver background.
  However, you could replace this with a bitmap specific for each forum
*/
td.rowpic {
		background-color: {T_TD_COLOR2};
		background-image: url(templates/subSilver/images/{T_TH_CLASS3});
		background-repeat: repeat-y;
}

/* Header cells - the blue and silver gradient backgrounds */
th	{
	color: {T_FONTCOLOR3}; font-size: {T_FONTSIZE2}px; font-weight : bold; 
	background-color: {T_BODY_LINK}; height: 25px;
	background-image: url(templates/subSilver/images/{T_TH_CLASS2});
}

td.cat,td.catHead,td.catSides,td.catLeft,td.catRight,td.catBottom {
			background-image: url(templates/subSilver/images/{T_TH_CLASS1});
			background-color:{T_TR_COLOR3}; border: {T_TH_COLOR3}; border-style: solid; height: 28px;
}

/*
  Setting additional nice inner borders for the main table cells.
  The names indicate which sides the border will be on.
  Don't worry if you don't understand this, just ignore it :-)
*/
td.cat,td.catHead,td.catBottom {
	height: 29px;
	border-width: 0px 0px 0px 0px;
}
th.thHead,th.thSides,th.thTop,th.thLeft,th.thRight,th.thBottom,th.thCornerL,th.thCornerR {
	font-weight: bold; border: {T_TD_COLOR2}; border-style: solid; height: 28px;
}
td.row3Right,td.spaceRow {
	background-color: {T_TR_COLOR3}; border: {T_TH_COLOR3}; border-style: solid;
}

th.thHead,td.catHead { font-size: {T_FONTSIZE3}px; border-width: 1px 1px 0px 1px; }
th.thSides,td.catSides,td.spaceRow	 { border-width: 0px 1px 0px 1px; }
th.thRight,td.catRight,td.row3Right	 { border-width: 0px 1px 0px 0px; }
th.thLeft,td.catLeft	  { border-width: 0px 0px 0px 1px; }
th.thBottom,td.catBottom  { border-width: 0px 1px 1px 1px; }
th.thTop	 { border-width: 1px 0px 0px 0px; }
th.thCornerL { border-width: 1px 0px 0px 1px; }
th.thCornerR { border-width: 1px 1px 0px 0px; }

/* The largest text used in the index page title and toptic title etc. */
.maintitle	{
	font-weight: bold; font-size: 22px; font-family: "{T_FONTFACE2}",{T_FONTFACE1};
	text-decoration: none; line-height : 120%; color : {T_BODY_TEXT};
}

/* General text */
.gen { font-size : {T_FONTSIZE3}px; }
.genmed { font-size : {T_FONTSIZE2}px; }
.gensmall { font-size : {T_FONTSIZE1}px; }
.gen,.genmed,.gensmall { color : {T_BODY_TEXT}; }
a.gen,a.genmed,a.gensmall { color: {T_BODY_LINK}; text-decoration: none; }
a.gen:hover,a.genmed:hover,a.gensmall:hover	{ color: {T_BODY_HLINK}; text-decoration: underline; }

/* The register, login, search etc links at the top of the page */
.mainmenu		{ font-size : {T_FONTSIZE2}px; color : {T_BODY_TEXT} }
a.mainmenu		{ text-decoration: none; color : {T_BODY_LINK};  }
a.mainmenu:hover{ text-decoration: underline; color : {T_BODY_HLINK}; }

/* Forum category titles */
.cattitle		{ font-weight: bold; font-size: {T_FONTSIZE3}px ; letter-spacing: 1px; color : {T_BODY_LINK}}
a.cattitle		{ text-decoration: none; color : {T_BODY_LINK}; }
a.cattitle:hover{ text-decoration: underline; }

/* Forum title: Text and link to the forums used in: index.php */
.forumlink		{ font-weight: bold; font-size: {T_FONTSIZE3}px; color : {T_BODY_LINK}; }
a.forumlink 	{ text-decoration: none; color : {T_BODY_LINK}; }
a.forumlink:hover{ text-decoration: underline; color : {T_BODY_HLINK}; }

/* Used for the navigation text, (Page 1,2,3 etc) and the navigation bar when in a forum */
.nav			{ font-weight: bold; font-size: {T_FONTSIZE2}px; color : {T_BODY_TEXT};}
a.nav			{ text-decoration: none; color : {T_BODY_LINK}; }
a.nav:hover		{ text-decoration: underline; }

/* titles for the topics: could specify viewed link colour too */
.topictitle,h1,h2	{ font-weight: bold; font-size: {T_FONTSIZE2}px; color : {T_BODY_TEXT}; }
a.topictitle:link   { text-decoration: none; color : {T_BODY_LINK}; }
a.topictitle:visited { text-decoration: none; color : {T_BODY_VLINK}; }
a.topictitle:hover	{ text-decoration: underline; color : {T_BODY_HLINK}; }

/* Name of poster in viewmsg.php and viewtopic.php and other places */
.name			{ font-size : {T_FONTSIZE2}px; color : {T_BODY_TEXT};}

/* Location, number of posts, post date etc */
.postdetails		{ font-size : {T_FONTSIZE1}px; color : {T_BODY_TEXT}; }

/* The content of the posts (body of text) */
.postbody { font-size : {T_FONTSIZE3}px; line-height: 18px}
a.postlink:link	{ text-decoration: none; color : {T_BODY_LINK} }
a.postlink:visited { text-decoration: none; color : {T_BODY_VLINK}; }
a.postlink:hover { text-decoration: underline; color : {T_BODY_HLINK}}

/* Quote & Code blocks */
.code { 
	font-family: {T_FONTFACE3}; font-size: {T_FONTSIZE2}px; color: {T_FONTCOLOR2};
	background-color: {T_TD_COLOR1}; border: {T_TR_COLOR3}; border-style: solid;
	border-left-width: 1px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px
}

.quote {
	font-family: {T_FONTFACE1}; font-size: {T_FONTSIZE2}px; color: {T_FONTCOLOR1}; line-height: 125%;
	background-color: {T_TD_COLOR1}; border: {T_TR_COLOR3}; border-style: solid;
	border-left-width: 1px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px
}

/* Copyright and bottom info */
.copyright		{ font-size: {T_FONTSIZE1}px; font-family: {T_FONTFACE1}; color: {T_FONTCOLOR1}; letter-spacing: -1px;}
a.copyright		{ color: {T_FONTCOLOR1}; text-decoration: none;}
a.copyright:hover { color: {T_BODY_TEXT}; text-decoration: underline;}

/* Form elements */
input,textarea, select {
	color : {T_BODY_TEXT};
	font: normal {T_FONTSIZE2}px {T_FONTFACE1};
	border-color : {T_BODY_TEXT};
}

/* The text input fields background colour */
input.post, textarea.post, select {
	background-color : {T_TD_COLOR2};
}

input { text-indent : 2px; }

/* The buttons used for bbCode styling in message post */
input.button {
	background-color : {T_TR_COLOR1};
	color : {T_BODY_TEXT};
	font-size: {T_FONTSIZE2}px; font-family: {T_FONTFACE1};
}

/* The main submit button option */
input.mainoption {
	background-color : {T_TD_COLOR1};
	font-weight : bold;
}

/* None-bold submit button */
input.liteoption {
	background-color : {T_TD_COLOR1};
	font-weight : normal;
}

/* This is the line in the posting page which shows the rollover
  help line. This is actually a text box, but if set to be the same
  colour as the background no one will know ;)
*/
.helpline { background-color: {T_TR_COLOR2}; border-style: none; }

/* Import the fancy styles for IE only (NS4.x doesn't use the @import function) */
@import url("templates/subSilver/formIE.css"); 
-->
</style>
<!-- BEGIN switch_enable_pm_popup -->
<script language="Javascript" type="text/javascript">
<!--
	if ( {PRIVATE_MESSAGE_NEW_FLAG} )
	{
		window.open('{U_PRIVATEMSGS_POPUP}', '_phpbbprivmsg', 'HEIGHT=225,resizable=yes,WIDTH=400');;
	}
//-->
</script>
<!-- END switch_enable_pm_popup -->
</head>
<body bgcolor="{T_BODY_BGCOLOR}" text="{T_BODY_TEXT}" link="{T_BODY_LINK}" vlink="{T_BODY_VLINK}">

<a name="top"></a>

<table width="100%" cellspacing="0" cellpadding="10" border="0" align="center"> 
	<tr> 
		<td class="bodyline"><table width="100%" cellspacing="0" cellpadding="0" border="0">
			<tr> 
				<td><a href="{U_INDEX}"><img src="templates/subSilver/images/logo_phpBB.gif" border="0" alt="{L_INDEX}" vspace="1" /></a></td>
				<td align="center" width="100%" valign="middle"><span class="maintitle">{SITENAME}</span><br /><span class="gen">{SITE_DESCRIPTION}<br />&nbsp; </span> 
				<table cellspacing="0" cellpadding="2" border="0">
					<tr> 
						<td align="center" valign="top" nowrap="nowrap"><span class="mainmenu">&nbsp;<a href="{U_FAQ}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_faq.gif" width="12" height="13" border="0" alt="{L_FAQ}" hspace="3" />{L_FAQ}</a></span><span class="mainmenu">&nbsp; &nbsp;<a href="{U_SEARCH}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_search.gif" width="12" height="13" border="0" alt="{L_SEARCH}" hspace="3" />{L_SEARCH}</a>&nbsp; &nbsp;<a href="{U_MEMBERLIST}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_members.gif" width="12" height="13" border="0" alt="{L_MEMBERLIST}" hspace="3" />{L_MEMBERLIST}</a>&nbsp; &nbsp;<a href="{U_GROUP_CP}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_groups.gif" width="12" height="13" border="0" alt="{L_USERGROUPS}" hspace="3" />{L_USERGROUPS}</a>&nbsp; 
						<!-- BEGIN switch_user_logged_out -->
						&nbsp;<a href="{U_REGISTER}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_register.gif" width="12" height="13" border="0" alt="{L_REGISTER}" hspace="3" />{L_REGISTER}</a></span>&nbsp;
						<!-- END switch_user_logged_out -->
						</td>
					</tr>
					<tr>
						<td height="25" align="center" valign="top" nowrap="nowrap"><span class="mainmenu">&nbsp;<a href="{U_PROFILE}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_profile.gif" width="12" height="13" border="0" alt="{L_PROFILE}" hspace="3" />{L_PROFILE}</a>&nbsp; &nbsp;<a href="{U_PRIVATEMSGS}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_message.gif" width="12" height="13" border="0" alt="{PRIVATE_MESSAGE_INFO}" hspace="3" />{PRIVATE_MESSAGE_INFO}</a>&nbsp; &nbsp;<a href="{U_LOGIN_LOGOUT}" class="mainmenu"><img src="templates/subSilver/images/icon_mini_login.gif" width="12" height="13" border="0" alt="{L_LOGIN_LOGOUT}" hspace="3" />{L_LOGIN_LOGOUT}</a>&nbsp;</span></td>
					</tr>
				</table></td>
			</tr>
		</table>

		<br />
Last edited by feyd on Fri May 21, 2004 6:48 pm, edited 2 times in total.
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

instead of including the file, they load it, and "compile" it, to use their internal terminology. Basically, they store an associative array of all the substitution names (minus the braces).. so something like:
source: page_header.php

Code: Select all

$template->assign_vars(array(
	'SITENAME' => $board_config['sitename'],
	'SITE_DESCRIPTION' => $board_config['site_desc'],
	'PAGE_TITLE' => $page_title,
	'LAST_VISIT_DATE' => sprintf($lang['You_last_visit'], $s_last_visit),
	'CURRENT_TIME' => sprintf($lang['Current_time'], create_date($board_config['default_dateformat'], time(), $board_config['board_timezone'])),
	'TOTAL_USERS_ONLINE' => $l_online_users,
	'LOGGED_IN_USER_LIST' => $online_userlist,
	'RECORD_USERS' => sprintf($lang['Record_online_users'], $board_config['record_online_users'], create_date($board_config['default_dateformat'], $board_config['record_online_date'], $board_config['board_timezone'])),
	'PRIVATE_MESSAGE_INFO' => $l_privmsgs_text,
	'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
	'PRIVATE_MESSAGE_NEW_FLAG' => $s_privmsg_new,

	'PRIVMSG_IMG' => $icon_pm,

	'L_USERNAME' => $lang['Username'],
	'L_PASSWORD' => $lang['Password'],
	'L_LOGIN_LOGOUT' => $l_login_logout,
	'L_LOGIN' => $lang['Login'],
	'L_LOG_ME_IN' => $lang['Log_me_in'],
	'L_AUTO_LOGIN' => $lang['Log_me_in'],
	'L_INDEX' => sprintf($lang['Forum_Index'], $board_config['sitename']),
	'L_REGISTER' => $lang['Register'],
	'L_PROFILE' => $lang['Profile'],
	'L_SEARCH' => $lang['Search'],
	'L_PRIVATEMSGS' => $lang['Private_Messages'],
	'L_WHO_IS_ONLINE' => $lang['Who_is_Online'],
	'L_MEMBERLIST' => $lang['Memberlist'],
	'L_FAQ' => $lang['FAQ'],
	'L_USERGROUPS' => $lang['Usergroups'],
	'L_SEARCH_NEW' => $lang['Search_new'],
	'L_SEARCH_UNANSWERED' => $lang['Search_unanswered'],
	'L_SEARCH_SELF' => $lang['Search_your_posts'],
	'L_WHOSONLINE_ADMIN' => sprintf($lang['Admin_online_color'], '<span style="color:#' . $theme['fontcolor3'] . '">', '</span>'),
	'L_WHOSONLINE_MOD' => sprintf($lang['Mod_online_color'], '<span style="color:#' . $theme['fontcolor2'] . '">', '</span>'),

	'U_SEARCH_UNANSWERED' => append_sid('search.'.$phpEx.'?search_id=unanswered'),
	'U_SEARCH_SELF' => append_sid('search.'.$phpEx.'?search_id=egosearch'),
	'U_SEARCH_NEW' => append_sid('search.'.$phpEx.'?search_id=newposts'),
	'U_INDEX' => append_sid('index.'.$phpEx),
	'U_REGISTER' => append_sid('profile.'.$phpEx.'?mode=register'),
	'U_PROFILE' => append_sid('profile.'.$phpEx.'?mode=editprofile'),
	'U_PRIVATEMSGS' => append_sid('privmsg.'.$phpEx.'?folder=inbox'),
	'U_PRIVATEMSGS_POPUP' => append_sid('privmsg.'.$phpEx.'?mode=newpm'),
	'U_SEARCH' => append_sid('search.'.$phpEx),
	'U_MEMBERLIST' => append_sid('memberlist.'.$phpEx),
	'U_MODCP' => append_sid('modcp.'.$phpEx),
	'U_FAQ' => append_sid('faq.'.$phpEx),
	'U_VIEWONLINE' => append_sid('viewonline.'.$phpEx),
	'U_LOGIN_LOGOUT' => append_sid($u_login_logout),
	'U_GROUP_CP' => append_sid('groupcp.'.$phpEx),

	'S_CONTENT_DIRECTION' => $lang['DIRECTION'],
	'S_CONTENT_ENCODING' => $lang['ENCODING'],
	'S_CONTENT_DIR_LEFT' => $lang['LEFT'],
	'S_CONTENT_DIR_RIGHT' => $lang['RIGHT'],
	'S_TIMEZONE' => sprintf($lang['All_times'], $l_timezone),
	'S_LOGIN_ACTION' => append_sid('login.'.$phpEx),

	'T_HEAD_STYLESHEET' => $theme['head_stylesheet'],
	'T_BODY_BACKGROUND' => $theme['body_background'],
	'T_BODY_BGCOLOR' => '#'.$theme['body_bgcolor'],
	'T_BODY_TEXT' => '#'.$theme['body_text'],
	'T_BODY_LINK' => '#'.$theme['body_link'],
	'T_BODY_VLINK' => '#'.$theme['body_vlink'],
	'T_BODY_ALINK' => '#'.$theme['body_alink'],
	'T_BODY_HLINK' => '#'.$theme['body_hlink'],
	'T_TR_COLOR1' => '#'.$theme['tr_color1'],
	'T_TR_COLOR2' => '#'.$theme['tr_color2'],
	'T_TR_COLOR3' => '#'.$theme['tr_color3'],
	'T_TR_CLASS1' => $theme['tr_class1'],
	'T_TR_CLASS2' => $theme['tr_class2'],
	'T_TR_CLASS3' => $theme['tr_class3'],
	'T_TH_COLOR1' => '#'.$theme['th_color1'],
	'T_TH_COLOR2' => '#'.$theme['th_color2'],
	'T_TH_COLOR3' => '#'.$theme['th_color3'],
	'T_TH_CLASS1' => $theme['th_class1'],
	'T_TH_CLASS2' => $theme['th_class2'],
	'T_TH_CLASS3' => $theme['th_class3'],
	'T_TD_COLOR1' => '#'.$theme['td_color1'],
	'T_TD_COLOR2' => '#'.$theme['td_color2'],
	'T_TD_COLOR3' => '#'.$theme['td_color3'],
	'T_TD_CLASS1' => $theme['td_class1'],
	'T_TD_CLASS2' => $theme['td_class2'],
	'T_TD_CLASS3' => $theme['td_class3'],
	'T_FONTFACE1' => $theme['fontface1'],
	'T_FONTFACE2' => $theme['fontface2'],
	'T_FONTFACE3' => $theme['fontface3'],
	'T_FONTSIZE1' => $theme['fontsize1'],
	'T_FONTSIZE2' => $theme['fontsize2'],
	'T_FONTSIZE3' => $theme['fontsize3'],
	'T_FONTCOLOR1' => '#'.$theme['fontcolor1'],
	'T_FONTCOLOR2' => '#'.$theme['fontcolor2'],
	'T_FONTCOLOR3' => '#'.$theme['fontcolor3'],
	'T_SPAN_CLASS1' => $theme['span_class1'],
	'T_SPAN_CLASS2' => $theme['span_class2'],
	'T_SPAN_CLASS3' => $theme['span_class3'],

	'NAV_LINKS' => $nav_links_html)
);
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post by McGruff »

Personally, I'd recommend sticking with embedded echo's if you're new to template engines.

Rather than eval'ing the template, define all the dynamic vars then simply include() the template in the same scope as the vars.

If you want to disentangle the code, it's important to keep presentation and business logic separate. Basically, biz logic defines a bunch of bare, unformatted vars, and presentation (html templates and some custom template functions) applies formatting to these vars.

Sooner or later you'll run into some issues.

For example, if you need to print rows of data - eg a table - you'll need some kind of template function. Perhaps the best way to deal with this is a decorated iterator. Define the collection (ie an iterator) in the biz logic, then, in the template, feed that into a decorator. A Table decorator could define a < table > html string for example (and is re-usable with any type of collection).

The other option is to define the collection as an array rather than an iterator, then write a presentation fn to loop through it, dumping the output in a string var which gets echo'd when the loop is done. This means you end up iterating through the collection twice - less elegant but unlikely to matter.

Incidentally, if you're using a "FrontController" type arrangement, you might want to validate application commands against a command dictionary - that can be the filesystem itself if you dynamically include page scripts etc (some is_file or file_exists checks).

All user input should always be validated.
User avatar
lazy_yogi
Forum Contributor
Posts: 243
Joined: Fri Jan 24, 2003 3:27 am

Post by lazy_yogi »

A couple of small things Skater,

1. you can make the php even less intrusive by using for printing variables with

Code: Select all

<?=$var ? >
instead of

Code: Select all

<?php echo $nvar; ? >
2. for the sake of correct code plus readability, I would suggest
a) assigning $GET to the varialbe instead of getting auto from $p, and
b) also using a meaningful variable name like $page. So instead of

Code: Select all

if(empty($_GET['p'])) $p = "home";
use

Code: Select all

$page = empty($_GET['p']) ? "home" :  $_GET['p'];
3. in your page class, where does $user[tpl] come from? Maybe in the constructor, set a class variable $user and in build use $this->user[tpl] so its clear where it is from and how it is used.



On the last note, heres a link to an incredibly simple templating engine that works for what you seem to be trying to do. It's extrememly basic and easy to understand. I think it's awesom.
http://www.sitepoint.com/article/1218
jason
Site Admin
Posts: 1767
Joined: Thu Apr 18, 2002 3:14 pm
Location: Montreal, CA
Contact:

Post by jason »

McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post by McGruff »

Just a quick note to say short tags can cause problems - they might not be enabled on all servers. If portability is an issue, it might be best to stick with < ?php ... ? >
d3ad1ysp0rk
Forum Donator
Posts: 1661
Joined: Mon Oct 20, 2003 8:31 pm
Location: Maine, USA

Post by d3ad1ysp0rk »

3. in your page class, where does $user[tpl] come from? Maybe in the constructor, set a class variable $user and in build use $this->user[tpl] so its clear where it is from and how it is used.
$user[tpl] will be an int, from the users config row, which is what template they chose to display the site in.
User avatar
Heavy
Forum Contributor
Posts: 478
Joined: Sun Sep 22, 2002 7:36 am
Location: Viksjöfors, Hälsingland, Sweden
Contact:

Post by Heavy »

In my template engine, which is the first I ever wrote, and the first OOP attempt I succeded with (that said, Iv'e told you it is a bit ugly), I do this:

1 Scan it for php code and disable it if found.
2 Parse template with lots of regex, breaking it down into small pieces.
3 substitute all template variable / conditionals and loop placeholders with real embedded php code.
4 concatenate all the small template pieces into one string, that looks pretty much like your first example above. (1.inc (template 1) ).
5 cache it in the server! Yeah! this is the part where I am proud.
6 run the script using eval, or just include it if cached.

Everyone should feel I am wasting time by using regex, but that is only the first time it is accessed, or when the template file has changed. Then it is almost as fast as PHP embedded in HTML. It just takes a timestamp comparison to know that the cached version is OK. Then it is included.

Fast and secure, if I let users apply custom templates.

There are some nasty design flaws I am ashamed of, so I don't post it just like that. But if you ask me kindly I might...

The "manual", written only for me to remember, goes:

Code: Select all

<?php
/*

Templating rules:
Control commands go inside closeable tags like DIV, TABLE, TR, TD and P
	and form elements like INPUT, SELECT and TEXTAREA

All varnames can take arrays, which can be multidimensional, and the indexes is given with this form:
varName:indexNameDim1:indexNameDim2:indexNameDim3
<html>
	<body>
		<div tplIfTrue="varName"> 
			This div-block gets printed if varname is true in template parser
			To echo the content of varName, do this: {varName}
		</div>
		<div tplLoop="arrVarName as varName"> 
			This div-block gets looped for each element of arrVarname
			There is no other kind of loop but foreach controlled from php.
			This for each loop provides a counter called counter:arrVarName. 
				It is incremented after every loop iteration and starts with value 0.
				If varName is an array (collection), 
					you access its elements with use of this syntax:
					{varName:strIndex}
					you can access content of multidimension arrays by referencing them with:
					{varName:strIndex1:strSubIndex1}{varName:strIndex1:strSubIndex2}
					
		</div>
		[include filename.tpl] 
			Includes template file with name filename.tpl here. I file is not found,
			the include command token is left as is in the template source.
	</body>
</html>

Beside HTML-element parameter control commands, there are template tags.
By default these tags have the format:
	{varName[]}	
	{? if-else-code }
	The delimiters may be changed by configuration in the php code.

There are short if-else statements, which use the form of the "Ternary Operator":
	{? boolVarName ? 'true' : 'false'}
	which is interpreted to:
	if boolvarname is true then print 'true' else print 'false'.
	The strings after ? and : may only be quoted with single quotes:
		example:
		{? boolVarName ? 'true' : 'false'}
		if you prefer variables, strip the quotes:
		{? boolVarName ? strIfTrue : strIfFalse}
	
	it can take simple comparison expressions aswell:
	{? counter == 1  ? 'selected' : ''}
	{? counter > 10  ? 'class="old"' : 'class="new"'}
	{? counter is_odd  ? 'class="oddrow"' : 'class="evenrow"'}
	{? counter is_even  ? 'class="evenrow"' : 'class="oddrow"'}
	
	it can take varnames as well, and those can be multidimensional arrays:
	{? counter:arrVarName == 1  ? arrVarName:strIfTrue : arrVarName:arrIfFalse:}
				
	If you like to print a '<' character, immediately followed by other characters, you may have to use < instead, which the parser ignores.
No PHP content may go into template:

Following code will be deleted from template:
	<?php
		this will be deleted
	?>
	<script language="php">so will this</script>
	<? and this ?>
	<% and this %>

*/
?>
Feel free to comment.
Also, I am not 100% sure the "manual" is up to date.
fastfingertips
Forum Contributor
Posts: 242
Joined: Sun Dec 28, 2003 1:40 am
Contact:

Post by fastfingertips »

I'm sorry to say but this way is a wrong one.

And i will try to explain myself and off course i will try to point you some future directions (as i already build one).
Anyway because my english is not so good i must warn you that i understood that you need a web content manager.

When you are designing this manager you will have to follow some major criterias:

- easy web site creation (you must be able to implement any kind of solution)
- a fast engine
- it must be able to work with a database that allow stored procedures (i have implemented my solution on a firebird database server but now i'm testing it on a mysql server);

When you will read that you will understand that the major problem is how you will manage the site content and based on this how to build all other operations.

That's why i recommend you a following structure:
- a tree based structure of content
- ability to create from your index (home) as many directories as you may need;
- this directories must have ability to keep files inside (web pages)
- ability to define a master class style for every directory
- ability to define a master header and a master footer for every directory
- ability to create page templates
- ability to access the info's from database and populate this templates (many pages may share same template for example);

Because of this and beceause sometimes the php echo function is very annoing i recommend you to build your own command system (here i have used a XML schema) and with this of course a parser.

For example a template in my engine looks like:
<html>
<head>
<engine:getstyle styleId="2"/> //will get css
</head>
<body>
----------- some struture
<img src="<engine:getImage imageId="23"/>"> //In this way i'm getting images
</body>
</html>

I hope that this will help you
dave420
Forum Contributor
Posts: 106
Joined: Tue Feb 17, 2004 8:03 am

Post by dave420 »

Evaling the whole script? Oy! That sounds like a processing and security nightmare. Surely, the best approach to a template is one that can intelligently (using regexps) create the outputted HTML document by itself, without having to resort to eval. eval === nasty.
User avatar
Heavy
Forum Contributor
Posts: 478
Joined: Sun Sep 22, 2002 7:36 am
Location: Viksjöfors, Hälsingland, Sweden
Contact:

Post by Heavy »

dave420 wrote:Evaling the whole script? Oy! That sounds like a processing and security nightmare. Surely, the best approach to a template is one that can intelligently (using regexps) create the outputted HTML document by itself, without having to resort to eval. eval === nasty.
If your post refers to mine, I'd like to say:

Using caching logic, the only overhead my templating system generates is
* instantiating the template object
* checking if the cached template should be used or if we should cache a new one.

I only use eval() if the setup is to use no caching. And If I do, the code is secure due to the fact the system just confimed it to be. So the eval() function does not bite me there...
dave420
Forum Contributor
Posts: 106
Joined: Tue Feb 17, 2004 8:03 am

Post by dave420 »

I'm not saying your code sucks or anything (heck - you're making your own templating engine as opposed to just downloading any old one of the net - kudos for that!), but have you considered any other approach than eval? It has inherent flaws in it, such as you are giving the entire processing capability of PHP to some dynamically-generated script (complete overkill for the job). How do you know the code is safe? Could someone not "slip" some errant PHP code in there somehow? Just wondering, that's all :)
User avatar
Heavy
Forum Contributor
Posts: 478
Joined: Sun Sep 22, 2002 7:36 am
Location: Viksjöfors, Hälsingland, Sweden
Contact:

Post by Heavy »

dave420 wrote:... but have you considered any other approach than eval? ... How do you know the code is safe? Could someone not "slip" some errant PHP code in there somehow? Just wondering, that's all :)
Stop wondering and read my previous posts...
I just said it is an option to depend on eval. And yes! Any php in my templates is disabled by my hopefully correct regexp.
User avatar
Buddha443556
Forum Regular
Posts: 873
Joined: Fri Mar 19, 2004 1:51 pm

Post by Buddha443556 »

Evaling the whole script? Oy! That sounds like a processing and security nightmare. Surely, the best approach to a template is one that can intelligently (using regexps) create the outputted HTML document by itself, without having to resort to eval. eval === nasty.
I must be doing something wrong!? I'm using both includes and eval() with my templates. It's just plain old PHP. My templates are used for seperating business logic from display logic. The templates are not intended to be modified by anyone but me. User data is only echo'ed and of course throughly cleansed before that even happens. I don't consider this a security nightmare. It seem much more simple than using regexp's. I thought (security) risk increased with complexity?

Now if I was going to let just anyone modify templates then I would need something much more complex.

FYI: eval() is used 9 times in 7 file in Smarty. THOSE EVIL B******S!!!
Post Reply