PHP and Heredocs
Pages: 1, 2
The HTML Side Of Things
At this point, we can assign chunks of text to PHP associative array variables by including external files which contain heredocs. We can expand variables within the assignments and can easily access the contents at runtime for debugging. Now that we have separated database calls from PHP classes, let's turn our attention to HTML.
There are several ways to separate the presentation of HTML from the logic of PHP, including Smarty, the well known template package. (Smarty has an extensive set of capabilities, and is worth exploring.) For this project, using heredocs and substituting database values for placeholders is sufficient and straightforward. Here is a simple HTML template file:
<?php
$template =<<<EOD_PD
<html>
<head>
<title>Photo Display: FP_PHOTO_ID...</title>
</head>
<body>
FP_TOOLBAR
<p>
<center>
<img src="FP_FULL" />
<br />
FP_CAPTION
</center>
</p>
</body>
</html>
EOD_PD
?>
In my project, this particular file is at
(project_path)/include/ui-default/en/photo-display.php. I
access the file within my PHP classes like this:
$display_photo_page = $this->get_template("photo-display.php");
where get_template() looks like:
function get_template($which_file)
{
global $FP_LANG;
global $FP_UI;
include
"/usr/local/php-files/include/$FP_UI/$FP_LANG/$which_file";
return $template;
}
Once again, it's straightforward. I have a couple of variables which
control the UI to display. My HTML UI could be simple or complex,
depending on the end user and their browser. For every UI, I have the
option of grabbing a template that matches the language being used on the
browser side. You can look at
($_SERVER['HTTP_ACCEPT_LANGUAGE'] to find the language being
used and default to something like en (for English) when
nothing is set. Keeping security in mind, I do not take the values of
$FP_LANG and $FP_UI directly from the user.
Instead, I use user side information such as browser variables and cookie
preferences as indexes into known good values.
The placeholders are consistent between languages and UIs. For this example, I used:
FP_PHOTO_ID, a unique photo id from the database;FP_TOOLBAR, a snippet of HTML, used for navigation and pulled in from another HTML template file;FP_FULL, a URL for a photo; andFP_CAPTION, a photo caption.
Filling In The Template
At this point we have seen a method to set a $RSRC array,
and our HTML template. The next steps are to
- make a database call,
- build an associative array with placeholders as keys and database fields as values,
- do substitutions on the HTML template, and
- print out the result.
In order to show these in context, here is a stripped down example from my production code. Here is a SQL call, defined in a resource file:
$RSRC['SQL_PHOTO_DISPLAY'] =<<<EOD
SELECT collection.collection_id AS collection_id,
collection.collection_dir AS collection_dir,
photo.comments AS comments
FROM photo, collection
WHERE
photo_id = '{$VALS['photo_id']}'
AND collection.collection_id = photo.collection_id;
EOD;
And here is the function that ties everything together:
/*
** display - show a photo
**
** $db - an object that contains a handle to a MySQL database
** $photo_id - user input (i.e. 000045) via a link (GET)
*/
function display($db, $photo_id)
{
$P = &$this;
$RSRC = array();
$VALS = array();
$VALS['photo_id'] = $P->check_id($photo_id);
$P->get_global_resources($RSRC, "d-photo.res", $VALS);
// toolbar is just a simple HTML menu. It gets substituted
// into the main page later
$toolbar = $P->get_template("toolbar.php");
$display_photo_page = $P->get_template("photo-display.php");
// a sample debugging call. Use tail -f on an Apache error_log
// to see the output
$P->p_dbg($RSRC, "photo display RSRC");
// $db is an object with an open handle into our database
$sql_result = $db->query($RSRC['SQL_PHOTO_DISPLAY']);
$row = mysql_fetch_assoc($sql_result);
$collection_dir = $row['collection_dir'];
/*
** in real code, we ask the database for the filetype, and
** map to a filename extension from that... here we're
** just coming up with a relative path, based on
** the collection directory and photo id.
*/
$photo_path = $P->get_web_fullsize($collection_dir) .
$VALS['photo_id'] . ".jpg";
// here is how we build up an array of replacements
// that will be done on the HTML template
$all_replace['FP_TOOLBAR'] = $toolbar;
$all_replace['FP_FULL'] = $photo_path;
$all_replace['FP_CAPTION'] = $row['caption'];
$all_replace['FP_COLLECTION_ID'] = $row['collection_id'];
// do the replacements (replace() shown a little farther down)
$P->replace($display_photo_page, $all_replace);
print $display_photo_page;
}
One last piece of code is the replace function. It
transforms HTML templates into database-driven output:
/*
** replace - do multiple replacements on $str, using $repl hash array
*/
function replace(&$str, $repl)
{
while (list ($key, $val) = each($repl)) {
$str = ereg_replace("$key", "$val", $str);
}
return $str;
}
Summary
In this article we have gone through the steps of separating program logic, HTML templates, and SQL statements. Our method hinges on a few key concepts:
- Use separate files for PHP classes, HTML templates, and Resource Files, which include SQL (and other) information.
- Employ heredocs to minimize ugly, heavily quoted, and backslashed assignment statements.
- Adopt associative arrays as containers for variables coming from the user side, as well as read-only resources derived from them. This greatly simplifies function argument lists.
I have written a
small example which illustrates this approach. The example
additionally separates the calling PHP file (in an htdocs/
directory) from the PHP classes. I also show a method to switch between
different HTML interfaces and different languages.
I hope this method helps you in your PHP scripting projects.
Daniel Smith is currently working on an Open Source web-based photography database in Apache/MySQL/PHP.
Return to the PHP DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 9 of 9.
-
dp independent interface?
2003-04-15 06:11:08 hmerrill [Reply | View]
First off I have to say that I find this article very interesting, and have downloaded the example so I can refer to it for an upcoming project I'm going to work on.
It's been probably a year and a half since I last used PHP for web dev, but when I did, I found that there was no "standard" database independent interface, so I ended up writing my own.
I see in this example that you use MySQL specific database calls, so I'm curious to know *why*. I realize it's easiest to just write code to *one* database, but it would be much more portable to write code to a database independent interface, so that changing to a different database would be trivial. Isn't there a "standard" database independent interface (like Perl's "DBI") for PHP yet? And if there is, why didn't you use it?
Thanks! -
dp independent interface?
2003-04-26 22:21:28 Daniel Smith | [Reply | View]
> I see in this example that you use MySQL specific database calls, so I'm curious to know *why*. I realize it's easiest to just write code to *one* database, but it would be much more portable to write code to a database independent interface
You are correct. It was something I could have done. It would have been adding one more variable/one more thing to explain to the article and demo. I felt I was introducing a few different concepts as it was, so I focused on them.
Daniel
-
Why not use php-style instead of perlish ?
2003-04-12 06:42:57 anonymous2 [Reply | View]
Most of your examples can be written using simple php-style for strings. This isn't possible in PERL, but in PHP thats easy.
Example:
$var="SELECT *
FROM $table
WHERE a='1' ";
is better readable in my opinion and don't introduce the <<< Statement which can be confusing to newbies.
Also you started with separation of code and presentation, but mix them in one file, its better to have seperate files, and next would be to use a simple Template system, there are lots available for PHP. Because only then you are save that the designer does not 'break' your code.
Greez
Daniel -
Why not use php-style instead of perlish ?
2003-04-14 12:05:23 chromatic |
[Reply | View]
Sorry, that's incorrect.
Perl handles multi-line variable assignments. I just tried it on Perl 1 -- from 1987 -- and it worked just fine. -
Why not use php-style instead of perlish (2)?
2003-04-12 06:55:56 anonymous2 [Reply | View]
Sorry was not detailed enough.
The example should look like this:
$sql="
SELECT *
FROM $table
WHERE field='1' ";
You can have singlequotes in it with no problem.
If you really need Doublequotes you can first try if you can use 'SELECT ... field="2" '; but then Variables are not evaluated. But in many cases you dont need the heredoc, which is essential for PERL, because there you can't use the multiline string like in PHP.
Actually you use a kind of Templatesystem, but it seems a little complicated. I meen you should n't have ANY php in the template but only marker. You can read in the file simply with:
$template_str=join("", file("...path/template.php"); Which also does the trick. And the Designer sees NO php at all.
In your template you can use some marker like {$var} for displaying content which is not php either.
On the other hand, heredoc are useful if you have strings with many quotes and so on.
Daniel -
Why not use php-style instead of perlish (2)?
2003-04-12 11:34:46 Daniel Smith | [Reply | View]
Hi, I am Daniel Smith, the author:
I use heredocs SPECIFICALLY so that I do not have to write things such as:
$sql="
SELECT *
FROM $table
WHERE field='1' ";
Which introduces a new level of quoting.
As far as reading complete templates into a variable, so that the template is free of PHP assignment statements.. Yes, that is true. It's a little more work, in a sense, if there is a middle section of the template that gets repeated.. it has to be broken out (via HTML comments or otherwise) so that it gets assigned to a different variable (or element in an associative array)
In other words, if you have a template of head, middle, and tail sections, you can break them up into seperate heredocs, or you can read a straight HTML file in and muddle through it, breaking up the sections afterward. Think of the middle section as any variable number of results (a table, a collection of photos, etc.)
You may want to take a look at my example demo file.
It is at: http://www.onlamp.com/php/2003/04/10/examples/hd-demo-1.0.tgz
as an aside: I did not get into caching in my article, but it is something I am working on for my project. I don't want to simply read in templates for every invocation if I don't need to.
The reason I approached things this way in the first place was because I wanted to keep my package as self-contained as possible (fewer dependencies for future admins to deal with), but I also wanted to handle different languages and interfaces.
Daniel Smith
-
Why not use php-style instead of perlish (2)?
2003-04-15 03:32:34 anonymous2 [Reply | View]
HEREDOC strings are very convenient to spit out HTML, or any lengthy string blocks and it's not Perlish (!) it's derived from UNIX shell's here-document syntax!! I use both Perl and PHP for my web projects and it is pointless to say that PHP is better than Perl (both languages are fun to program with, and PHP really needs to level up with Perl regarding OOP).
Please drop by "http://theperlreview.com/" and read Ed Summer's article "Paying Homage to Perl (PHP) " in V.0-I.7 -
Why not use php-style instead of perlish (2)?
2003-04-15 09:48:25 Daniel Smith | [Reply | View]
Yes, I completely agree. I think you meant to reply to a different message in the thread, but I know what you mean.
My first exposure to heredocs was in the early 1980's, writing csh scripts. It's one thing missing in C/C++ that I really would have enjoyed having from time to time. Come to think of it, it would have been nice in Java, Javascript, and Tcl too.
A few languages supporting heredocs:
sh/bash/ksh
csh/tcsh
perl
php
python
One of many things I like about heredocs is that using them is much less cluttered than a series of print/echo statements. It allows me to better concentrate on the content within the heredoc, such as an email message, a help blurb, or anything else that tends to be multiline.
> Please drop by "http://theperlreview.com/" and read Ed Summer's article "Paying Homage to Perl (PHP) " in V.0-I.7
Interestingly, that PDF file contains an article "Separating Code, Presentation, and Configuration -- brian d foy" which makes use of heredocs in Perl :-)
Daniel Smith



Stellen