When I write PHP code for websites, I don't like mixing business logic with the presentation layer and as such I tend to create markup templates. I've written a very lightweight template engine to facilitate this, since I really don't want to move to a fully-fledged template framework like Smarty.
Here's a simplified example of what I do:
function renderTemplatePage($page, $params)
{
$page = readTemplateFile("templates/{$page}");
$tokens = getTemplateTokens($page);
foreach($tokens as $token)
{
if(substr($token, 0, 6) == "%_TPL_")
{
$subPage = renderTemplatePage(tokenToPageName($token), $params);
$page = str_replace($token, $subPage, $page);
}
else
{
$page = str_replace($token, $params[$token], $page);
}
}
return $page;
}
Sample page:
<html>
<head><title>%_PageTitle_%</title></head>
<body>
<div id="header">%_TPL_Header_%</div>
<div id="content">%_TPL_Homepage_%</div>
<div id="footer">%_TPL_Footer_%</div>
</body>
</html>
A call to renderTemplatePage("index", array("PageTitle" => "Home")) would produce a page entitled "Home", with content from the Header, Homepage and Footer templates.
I do all of my logic (including db queries, etc) before calling the rendering, so I can mass up a large $params array and just do a single call to render it all.
Are there any flaws in this methodology? Is there a more standard way to do this?
Its flawed. How do you handle template specific logic . Just out of curiosity how would you handle ifs or loops
There are several ways of achieving separation between logic and representation (it's formally known as part of MVC - Model-View-Controller methodology).
One direction I would like to point you in is XML+XSLT. The idea is to assemble all the information you need as an XML string (e.g. have a look at the output of the following URL: http://www.whiteoctober.co.uk/?dumpXML) , and then perform an XSLT transform on it (see http://www.w3schools.com/xsl/xsl_transformation.asp).
Templating in PHP is a religious debate - one I would rather not get dragged into. Suffice to say I'm strongly against Smarty - it's bulky and does nothing that standard PHP does not already do.
The problem with all these homebrew templates is lack of real life testing.
Instead of doing such a test you are coming here to ask other people.
It is not so wise strategy in general, as your own experience is indispensable, while there are not so much pro's hangs around having enough time to write you an extended answer. While there are always lots of inexperienced users ready to answer.
Are there any flaws in this methodology?
yes, of course.
You are declaring that you don't like mixing business logic with the presentation layer.
But whati is the content of %_TPL_ and where it written? In the same old business logic, I suppose. So, where is your desired separation then?
Related
I'm trying to find the best way of structuring a multi-language site navigation,.
I'm aware of the language class in CI but it seems to be more for defining random words and lines of text that are used commonly throughout the site. It appears that creating lang files for each language and then defining translations of all the links seems like the standard approach?
In past on non-codeigniter projects I’ve setup one class like this
class Link{
var $name = array();
var $url;
var $links = array();
function add_link($links){
$this->links[] = $links;
}
}
$all_sections = array();
$section = new Link();
$section->name['en'] = "Home";
$section->name['fr']] = "Uberdurky";
$section->url = "/";
$sub_section = new Link();
$sub_section->name['en'] = "About Acme Ltd";
$sub_section->name['fr'] = "Fabuka Acme Ltd";
$sub_section->url = "/about/";
$section->add_link($sub_section);
Then I have a function to loop through and output the nav, which just looks at the current name[Lang] as defined by session or URL
This to me seems simpler and less overhead - with the benefit that both the nav structure and translations are defined in one place. But I’m new to CI so I might be misunderstanding the standard approach… ? I've googled quite a bit and haven't seen a solution here in detail.
The important thing is that it works for you. There are a lot of benefits to using separate language files:
Clean separation
Only load what you need
Easy to keep track of which languages are available
Ability to let others easily translate the files
I don't see anything wrong with the way you're doing it, but if you want to optimize - don't bother defining all the different language lines. You don't need the French version defined if the language is English. Use only the ones you need, you shouldn't have to pass the whole array to add_link(), the Link class should be detecting the language and loading the appropriate array only...
...it's starting to sound like a language file might be a good idea actually.
For now you just have French and English. I'm assuming you know both languages and (Uberdurky?) are the only one working on this aspect, so it's easier for you to define them "inline". What happens when you want to support 3, 4, or 10 languages? Things will quickly become disorganized and cluttered.
However, you don't have to use the Codeigniter Language class, you might be better off using your own system for something like navigation, which tends to be littered with 1 or two word translations, and changes somewhat frequently (either per site or between sites).
Once again, it's your call. Do what works best for you now and optimize later.
This might be helpful to anyone coming across this question.
https://github.com/cflynn07/CodeIgniterInternationalizationUtility
It's a script to take a cleanly structed HTML table of language translations, and convert them into the required language files used in codeigniter.
I'm having a hard time saving my template to a database and download the contents to view using Smarty.
I followed the tutorial on Smarty.net on how to make a custom retrieve type.
My original method was save the tpl markup to the db then download it and pass it as a variable to a smarty assigned variable and in my text.pl do something like {$source}
but this parses it as a string and smarty won't compile it.
the tutorial on smarty website didn't help and just leaves me with more questions...
this is what i tried based on the tutorial
function db_get_template($tpl_name, &$tpl_source, &$s)
{
$tpl_name = "index.tpl";
$tpl_source = nl2br($defaults->get_default_theme('theme_data'));
return true;
}
function db_get_timestamp($tpl_name, &$tpl_timestamp, &$smarty)
{
$tpl_timestamp = (int)time();
//$tpl_timestamp = (int)$defaults->get_default_theme('modified_on');
return true;
}
function db_get_secure($tpl_name, &$smarty)
{
// assume all templates are secure
return true;
}
function db_get_trusted($tpl_name, &$smarty)
{
// not used for templates
}
$s->registerResource("db", array(
"db_get_template",
"db_get_secure",
"db_get_trusted"
));
$s->display("db:index.tpl");
i'm not sure where db:index.tpl is being pulled from. I don't know where the markup is suppose to be loaded into...
I feel dirty even suggesting this, but:
Here's a more relevant manual page than the page you were referencing.
In particular, pay attention to the string: prefix to the "filename" passed in the display method call. You can simply pull the template data out of your database (a horrible idea, IMNSHO) and pass it to Smarty that way. No need to build your own custom resource thing unless you really want to do it that way.
I, for one, would urge you to keep it as stupid, ugly and obvious as possible, as to remind yourself about how much of a horrible, no-good idea this is. I'd like to remind you that Smarty compiles to PHP, and placing Smarty code in the database is an effective gateway to arbitrary code execution, a severe security vulnerability.
I have been looking online for a tutorial to build a template engine. I know there are many engines that exist, like smarty, twig, and pattemplate, that could do exactly what I want, but I am looking to learn how to build one. I started with a template engine that added strings to an array and then displayed the array. Since then I built one using eval() (see below).
<// Define links & folders
define("ROOT_HTTP", "http://" . $_SERVER['HTTP_HOST'] . "/preprocessor");
define("TEMPLATE", "/template");
// Get the template file
$template = file_get_contents("template/template.php");
// Replace
$template = str_replace("<x Title x>", displayTitle(), $template);
$template = str_replace("<x Menu x>", displayMenu(), $template);
$template = str_replace("<x Content x>", displayContent(), $template);
$result = #eval("?>" . $template . "<?");
function displayMenu(){
return "Link1<br />" .
"Link2<br />" .
"Link3<br />";
}
function displayTitle(){
return "Site Title <?php echo date(\"m-d-y\", time()); ?>";
}
function displayContent(){
return file_get_contents("content.php");
}
It works fairly well but its not what I am looking to achieve. I would like to build something that is like the Joomla template with tags like <jdoc:include type="component" />. I would also like it to be able to handle errors inline meaning that it will display the line number of an error or when I call echo "text" it displays text in the correct position inside the template.
How do I create something along those lines?
http://www.phptal.org/ sounds very similar and has good code organization. if extension of mentioned system does not suit the needs, it would at least work as good tutorial
First of all: Immediately forget the idea about using a TE with XML-like tags. Really, it may look nice on the first glance but only causes too much work in the end and is really limiting.
Secondly I obviously recommend you to use Twig. It is clean, fast, extensible and offers all the features you need.
And lastly: I have written a small tutorial how to write a simple but powerful TE in another Stackoverflow question. It is really simple but for smaller projects it may suffice.
I cannot agree with NikiC's point of view.
XML is, although an old syntax, very powerful and brings a lot of advantages -- one of which is its similitude with properly written HTML.
There is nothing limiting in using an XML-based template syntax.
Besides, although Twig is, indeed, an excellent and famous project, it still lacks from a really good separation paradigm. It is still too dangerous and too easy to make mistakes from within the template and cause damages to the application as a whole.
Finally, the best template engine -- just as the best MVC framework -- is the one you feel really comfortable with.
I recommend having a look at FigDice]1, which was inspired by PHPTal, but takes things a few steps further, with an exclusive approach by giving the Web Designer (integrator, html-ist, etc.) a central position with the project -- much more flexible than the Twig-like approach.
I would be happy to read some feedback.
Thanks
I am building a English/french website and was wondering if there is a best practice for such a job.
Duplicating the site and making a french and english folder with the appropriate site inside.
Using PHP to swap the content with html tags.
eg. if($lang=='en'):
Use php to swap only the content leaving the html tags the same for both. eg. if statements all over the place. Would this be bad for efficiency?
Any other suggestions would be appreciated
We have a framework in place for when (if) our site goes international that works like this...
Folder structure;
/
lang/
english/
images/
text/
dutch/
images/
text/
Any text or images that are language specific are removed from the page directly and replaced by constants. eg On the login screen, we drop in;
echo TEXT_LOGIN_WELCOME;
which is defined in /lang/english/text/login.php as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login:');
but in /lang/dutch/text/login.php it's defined as;
define('TEXT_LOGIN_WELCOME', 'Welcome, please login (in dutch):');
;-)
Each language define file is named exactly the same as the page it is used for, so when we load a public-facing page, we only need to figure out which language the user speaks and we can include the relevant language define file.
The good thing about this system is that all the language info is centralised. When you need to add a new language, simply copy the main (english?) folder, rename it, zip the whole thing up and send it to a translation service to work their magic. Of course, the downside of this system is the maintenance as both languages and content grow... If anyone has any bright ideas with regard to this then I'd love to hear them!
Btw, if you end up needing to guess a user's location by IP, you might want to check out geoIP.
Use a templating system. Smarty Template Engine is probably one of the most well-known PHP ones. Not only does a templating system serve the exact purpose you're trying to accomplish, it also makes maintaining pages far easier by separating the display code from the content (which also allows you to use the same template for lots of different content pages of a similar nature).
As the simplest way I recommend you to use i18n internationalization method & gettext catalogs (.po files).
The famous WordPress project is using it as well.
1 - Duplicating the entire site will force you to repeat every code touch-up into the 2 folders :-[
2 - If you mean somenting like
<?php if($lang=='en') { ?>
<p>English text</p>
<? } else { ?>
<p>Text français</p>
<? } ?>
This solution is perfect to manage two languages in the same page.
But you still have duplicated tags.
3 - Change only content it's really satisfaction.
Maybe proliferate of if statements can weigh down php compiling... I don't know.
Anyway document can be more concise with this approach:
<?php
function interpreter($buffer) {
$pieces = explode('#', $buffer);
if (isset($_GET['lang'])) $begin=$_GET['lang'];
else $begin = 1; // 1 to display français, 2 to display english
$tot = count($pieces);
for ($i=$begin; $i<$tot; $i+=3) { // remove one language
unset($pieces[$i]); }
foreach ($pieces as $value) { // recompose the text
$output .= $value; }
return $output;
}
ob_start("interpreter");
?>
#Français#English#
<p>#English text#Texte français#.</p>
<?php ob_end_flush() ?>
The text between ob_start and ob_end_flush is parsed AFTER php compiling.
That means are affected strings coming eg. from echo statement, not inside < ?php ?> tags.
Also content coming from php include IS affected.
But NOT external css or javascript.
Keep attention delimiter # isn't a caracter yet used elsewhere.
Maybe you'll prefer to replace with || or ^^
Of course in the future you can adapt this solution into 3 languages or more. But if you have to insert the "Third language translation#" in many lines of a big site, maybe the solution from MatW fits you.
I'd like to adhere to the Don't-Repeat-Yourself principle, but sometimes when I write PHP together with HTML and CSS, if I re-use the same code for different situations, my code soon will have so many if-then-else that the code is not easily maintainable.
This may be a bigger issue if Smarty, the templating engine is used, because most code editor won't match up {if} {else} {/if} So the programmer needs to look for the matching tag visually, and is not easy when there are 3 or 4 levels of nested {if} {else} {/if}.
In such situation, is there a way to stick to DRY, but still have good maintainable code?
Just so we have more info... What program are you using to write your code?
Tips for not repeating yourself:
Use some sort of templates. Doing this keeps you from having to repeat code for displaying content in each of your pages. I.E. If you have a site with 20 pages and you decide to change your layout, you don't want to have to go through and then change all 20 of your pages.
Use functions. If you have code that performs a specific task, DON'T write that code multiple times throughout your program/page. Create a function and then call it in each spot where you need that task performed. That way if you need to make a change you just modify that one function and don't have to search through your code to find every place that you performed that task. If you know about classes & methods (a method is a function in a class), for many tasks, this is even better as it provides you with data encapsulation and allows you to group related functions together so that you can include the class in future projects as needed.
If you are having difficulty with lots of if/else statements and code not being very readable there are a few things you can do:
1. Consider trying a new editor. Code folding is a must. Some editors also have vertical lines that highlight and match up indented code so you know what goes with what. If you want a decent free editor, I would recommend Notepad++ as it has both these features (just google it, I can't add links here).
2. There are techniques you can use to reduce the number of nested if statements that you have...
Example (this code):
if (item1 == true) {
if (item2 == true) {
item3 = 5;
}
else {
item3 = 10;
}
}
else {
if (item2 == true) {
item3 = 15;
}
else {
item3 = 20;
}
}
Can also be flattened out into:
if (item1 == true && item2 == true) {
item3 = 5;
}
else if (item1 == true && item2 == false) {
item3 = 10;
}
else if (item1 == false && item2 == true) {
item3 = 15;
}
else {
item3 = 20;
}
So if you have 3 or 4 levels of nested if/elses and you want to flatten them out, you may find your code more readable to use multiple arguments such as above. It does the same thing, it's just a matter of preference for which way you do it.
Try and not mix your logic (I'm assuming PHP) and your display (I'm assuming HTML/CSS). This is not always easy to do, but using templates and css, it is possible. Let me give you a practical example of how you can do this on a home page that displays a users name as a welcome message.
Inline PHP (try to avoid):
<html>
<body>
<?php
if ($logged_in == true) {
echo "Welcome ",$user->name(),"!";
}
else {
echo "Welcome Guest!";
}
?>
</body>
</html>
Logic separate from display (better):
<?php
if ($logged_in == true) {
$greetingMessage = "Welcome ",$user->name(),"!";
}
else {
$greetingMessage = "Welcome Guest!";
}
?>
<html>
<body>
<?php echo $greetingMessage; ?>
</body>
</html>
^ Keeping your logic separate from your view (HTML) will help you not get overwhelmed when your project starts getting really complex. It's best just not to mix! :)
Good luck man!
Short of a full framework, what I tend to do for content (even if it contains logic) is separate it out into files and use another logical evaluation to merge them together (mangle them) and then evaluate the templating logic after that. This chunkifies your content and makes chunks sharable / reusable on common state.
This way each final template buffer is a flattened tree of discrete re-usable content nuggets that you can store on disk or a database. Even something as simple as a little parser that replaces:
<h1>{{insert:shared_page_header}}</h1>
With shared_page_header.txt helps keep things separate. It also forces you to look at separation on concerns even in the logic that is embedded in your templates. Manageable, reusable chunks of anything (dynamic or not) are always the way to go. Your templates are just strings until evaluated, so treat them as shared components merged into a big-dirty-string(TM) and then evaluated.
Good Luck
The first sentence of your question is the problem, IMO. You really shouldn't be mixing code with your view (i.e. HTML and CSS). There are several PHP MVC frameworks that will help you not do this. The Zend framework is pretty decent but there are others as well.
If you don't want to use a framework, then I'd suggest refactoring your pages to not use code like this and call objects on the back end. Mixing your code and view quickly becomes unmaintainable in any language, not just PHP.
Now I may not be that familiar with implementing OOP concepts using PHP, but refactoring those nested if-else statements and placing them in a well-named function helps a lot in keeping up with the DRY principle. And besides, sticking with the DRY principle does make your code maintainable.
With an example script file it would be a lot easier for us to point out where you are going wrong, however some things which may or may not help you depending on what you are trying to achieve:
Taking a look at alternative syntax for control structures. Some people prefer to use this style when writing files which mainly contain HTML, with PHP being used only to decide which HTML sections to output.
Split up the reusable sections of code into files which you can later include, ie. header.php, footer.php etc.
Using an IDE which supports code folding
Good luck
If statements are considered to be logic statements, they should be avoided in views as much as possible as they violate MVC principles.
Switch to view helpers instead, for example:
class LoggedUsernameHelper
{
protected $user;
public function __construct(SomeUserObject $user)
{
$this->user=$user;
}
public function __toString()
{
return $user->isLogged() ? sprintf('Welcome %s', $user->getUsername()) :
'Welcome guest';
}
}
This code is reusable, testable, will keep your code dry :)