«
Dynamic Metadata
Contents
Introduction
Metadata is a huge buzzword. I’ve even seen sites that have these cool little <div>
boxes with a Meta
title on them. Sigh. This article, and the previous
two, are really all about metadata. HTTP headers are metadata,
and so is a DTD. The <head> element is almost pure metadata.
But all of this leads to the real thing, which is what you’re reading
now — the content of this document.
In this lab I will describe how to use PHP to create dynamic XHTML
<head> elements so that they are both consistent and unique for each page on your Web site, just like
I’ve done for this one. Have a look at the PHP source code, the resulting XHTML and, if you’re
browser supports it, the Page Info
screen for examples. If metadata interests you, try the
Semantic Data Extractor,
which is also located on the sidebar and can be run from any page on this Web site. It’s an
interesting tool from the W3C, using XSLT technology to transform an XML file
(which is what an XHTML document really is) into some other format such as HTML. The extraction is
designed to target specific document elements.
FYI: This is my XMDP Profile.
See Also: The Semantic Web.
Lab Progress
Let’s have a look at what we have in our document structure so far:
It may not look like much like much considering the work put into it so far. But nothing else would function without it. And if you take a quick look at the minimal framework of a valid XHTML document:
we’ve covered some ground. The next step is to add the <head> element. For
this lab the following elements, which are all contained within the <head>,
will be implemented:
<title>...</title><meta>...</meta><link>...</link><style>...</style><script>...</script>
Note: If you read the introduction
to this series you should recall that as part of the structure module
the <head> element is required, and within that element the <title> is also required.
The other elements are optional, even though I’ll be implementing the majority of them.
I won’t bother with the <base> element for instance, and leave it up to you to research and
include it if you see see the need.
This article is not a tutorial on SEO, so you’ll have
to look elsewhere for information on getting your site ranked. There are countless sites
and services out there for that, and most of them are after your money. However, the following
code will allow you to create custom <title>, <meta> keyword, description, category, (and so on)
elements for each and every page on your Web site. Or allow you to fall back on defaults if
you’re too lazy to do every one. Like me.
So depending on how detailed I need the document to be, most of my pages include a $page
hash variable. Recall that a PHP hash (associative array) is just a list of related things
indexed by strings. Although it may contain other values, for this lab the $page hash
looks like this:
$page['title'] => 'My Page';
$page['description'] => 'description text...';
$page['keywords'] => 'keyword1,keyword2...';
$page['category'] => 'INDEX';
Go figure! Since head() is a function in my preamble.php library, which is the same module
that contains the doctype() function we discussed in the previous lab, $page
will only be visible if we declare it globally in that function. In fact, you’ll see $page
crop up often in other functions as I move forward in this series. The final member of the
$page family is an index into another PHP hash, and one we’ll take a brief
look at in a minute.
The head() Function
I declare the function:
$css = null, $js = null) {
global $page, $media_type, $charset, $lang, $ent;
As you can see, it accepts two parameters $css and $js. Why null
(cough) values for them? Because if you don’t send anything when you call the head()
function they’re simply ignored. I’ll get to that in a bit too.
The first line of code in the function declares a list of global variables, which includes the $page
hash and the $media_type, $charset and $lang variables from the previous lab.
The final global $ent is related to the fact that I’m using the UTF-8 character set.
It’s a sad fact that named character entities are not universally supported by browsers. And I don’t
happen to be able to recall what the hexadecimal codepoint of a left double curly quote (“)
is off the top of my head so what I’ve done is created both a MySQL
table and a library function that maps pseudo named characters to their numeric equivalents.
So with a slight variation in syntax I can continue to use them and not have to worry about browsers
that lack support for this feature.
FAQ: What is a named character entity? evolt.org has a page with a discussion on this topic, some helpful links and a nice entity chart. I’m not a big fan of repetitive cutting and pasting so I’ve taken this one step further. UTF-8 is becoming more and more common, so unless you prefer seeing lots of this: ?????? I recommend setting your browser to Unicode and download a font that supports the full range of characters. There are a few in the public domain, try a Google search to find one.
The Code
As in the previous lab on DTD declarations, I’ll be discussing the code with links to line numbers in the source. So you can open a new window or tab to it, or switch back and forth as you read the this documentation.
On lines 4 and 5 I combine the $page['title'] string with the domain
name of my site (or omit the title if I’ve neglected to set it before calling the function). On
lines 7 and 8 I set a default value for the description, then check if there’s a custom value
coming from the $page hash. This same pattern continues with keywords on lines
12 through 15.
Now there are all sorts of guidelines for this stuff, but as I mentioned earlier this is not a
tutorial on SEO. While most experts
say that using <meta> keywords
is no longer worthwhile, I decided to implement them anyway as I will be using this feature to create
my own site indexing and searching features. This also holds true for including a <meta> category,
which is based on a subset of the ODP’s (dmoz.org) RDF database. This is
definitely another biggie on my to-do list. Visit the Open Directory Project
about page for additional information.
So with the <title> and <meta> description, keywords and category ready to go there’s one more detail
to tackle before I begin returning the <head> element. That is my copyright statement, which
I’ll take care of on lines 31 through 38. I set my name and domain, then call PHP’s date() function
with the format string 'Y' as the only parameter to grab the current 4-digit year. I
suppose you could hardcode the year and then go back and update it every January but I consider
laziness a virtue.
With a little trickery, and two more calls to the implode() function (which I hope you recall from
the last article) I’ve built my $copyright string. Notice that this is also a global because
I will be using it again later. Using PHP’s heredoc syntax starting on line 40 I now return
the majority of the <head> element back to the user agent that made the request (normally a browser).
It includes:
- the
<title> <meta>http-equiv$media_typeand$charset<meta>http-equiv$lang<meta>$description<meta>$keywords<meta>$category<meta>$author<meta>$copyright
And a few of the other usual suspects such as my favicon. Which brings us to CSS and
the <link> element. I use one base stylesheet that every page shares (which I call root.css,
go figure) and another for IE. I suppose I could use a server-side technique to
import it but the damn browser has to contribute something since it is
such a headache to deal with. So I use a conditional comment stylesheet only IE will see.
Recall that there are two optional parameters I can pass to the head() function. Both are arrays (and it is perfectly okay to pass an array with one member) that represent additional stylesheets and/or Javascript modules. I’m a big fan of modular programming, and if you’ve been through the CSS Labs already you’ll know I have a bunch of classes for lists, source code (and forms and tables even though I didn’t discuss them). At any rate, these are all useful, but it isn’t necessary to include them unless I need to. This cuts down on bloat, makes pages load faster, and keeps things in neat little packages. But I wouldn’t exactly call this encapsulation since I’m giving away all my secrets.
On line 61 I create a little hash of folders where I keep these things, on line 68 I call
the PHP functions is_array() and count() to make sure I have an array with at least one value
(see above) and then open the <style> tag on line 69. On line 70 I loop through each
CSS module, build the URL and @import it.
I’m not going to get into the DOM and ECMA/Javascript in this series, but on line 79 I use the exact same technique to dynamically import any of those libraries I may need.
On line 86 I close the </head> element and the head() function ends on line 88.
This page requires the CSS libraries for lists and source code, so I call the head()
function like this:
head(array('lists', 'code'));
And the resulting XHTML looks like this:
You can view the full source code to this page and the resulting XHTML by using the buttons to the left as mentioned earlier in the series.
And next? The <body> element naturally...





















































































