PHP and Heredocs

PHP is wonderful for generating dynamic web pages. The ability to
combine the PHP, HTML, and SQL statements within a script provides a lot
of power. Combining everything in one file, however, can make a big
mess.

Having separate files for PHP classes, HTML blocks, and SQL statements
can go a long way towards cleaner, more understandable web application
design and implementation. This article explains the use of
heredocs and associative arrays in "resource files".

This is a simple example, lacking JavaScript or fancy HTML/CSS, but you
can see how quickly things can get out of hand. Jumping between languages
can be fun for a quick and dirty script, but it's a nightmare to navigate
for anything substantial. You could go back and forth between separate
sections of HTML and PHP within a file, to eliminate using
print to produce HTML, but even this approach can be
messy.

Another problem is dividing the work. Look again at the example and
imagine a team consisting of a database person, a great client-side
HTML/UI designer, and you, the PHP coder in the middle of it all, trying
to glue everything together. How can the three of you make much progress
with everything stuck together in the same file?

When you extract hardwired HTML and SQL from your PHP application, you
give yourself a lot of flexibility. Start treating these separate parts
of an application as "resources" that can be loaded from separate files.
Among other benefits, separating these concerns allows you to easily
switch between different HTML UIs, each available in multiple languages.
Your database person can optimize statements without affecting the
client-side designer, and you can spend more time dealing with straight
PHP.

What technique should we use in our separate resource files to make
large blocks of SQL and HTML available to our PHP classes?

Heredocs!

Consider large chunks of HTML or SQL statements larger than a few
lines. How would you assign these to a variable? One approach is to
escape every quote:

$some_var = "select * from photos where comment =\"some comment\";";

That's a simple example. Once you start mixing single and double
quotes, for example, in names such as "O'Reilly", the potential for
missing backslashes increases.

Heredocs have no quote problems. They allow you to assign a variable
with one or more lines of text in a much more straightforward fashion. I
think of a heredoc as a "document, right here". Here is a simple example
of assigning a SQL statement to illustrate the syntax:

EOD can be thought of as "End Of Data". You can choose
any terminating string you wish, as long as it

is consistent in the first and last lines of the assignment, and

does not otherwise occur at the start of a line within the here
document.

My next example is much more complex and demonstrates the use of associative
arrays and quoting. I scrub input variables (checking them for
validity) from a form, and toss them into an associative array called
$VALS. Associative arrays make very convenient containers
for related variables that have been checked and need to be passed to
different functions.

Heredocs allow you to concentrate on the contents of the variable,
worrying less about print statements and backslashes. For large blocks of
HTML, this is crucial. A client-side person can focus on what they're
doing, instead of getting involved with the PHP side of things.

Resources And Templates: Using Heredocs

Let's shift our focus to using include files. When a file is
included, we can pick up variables from it. Rather than dealing with
myriad variables, each resource I pick up from my include file becomes an
element in an array called $RSRC. Bear in mind that
once-defined, you will treat this array as read-only.

Let's look at a function within a PHP class that reads in a resource file:

/*
** get_global_resources - update $RSRC[]
**
** This is intended for resource files that
** do not change from one language or theme to another.
** One example is SQL statements
*/
function get_global_resources(&$RSRC, $context,
$VALS = array())
{
// $VALS is an associative array, it allows us to
// expand vars of the form: $VAL['foo'] within the
// resource file..
include "/usr/local/php-project/include/global/$context";
}

It's a pretty short function. I can optionally pass in an associative
array, $VALS, for use in expanding variables within
resources. This is what a call to get_global_resources()
looks like:

While I show the use of variables in an include
path, it is crucial to note that values (such as $context in
the get_global_resources() function) come from predefined
constants and configuration values. If user values are to affect an
include path in your web app, then user values should serve as an index
into an associative array with known good paths. Never have user-values
map directly to an include path without first checking their validity.

Some quick notes:

Just like any other include file, you do not want to have any extra
characters after the closing ?>

$VALS is used to expand variables within a given resource.
This affects the timing of when you include your resource file, as you cannot
expand a variable which has not yet been defined. Sometimes you must break one
resource file into smaller chunks to address this.

What Do The Included Resources Look Like?

Now that we're pulling in things from resource files, it's helpful to
check on the $RSRC variable. I use the Apache web server and
like to scan the logs with tail -f logs/error_log to see
what's happening. Rather than cluttering up the browser side with a lot
of diagnostic output, I use:

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:

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; and

FP_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:

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.