Making a Font Selector
We recently made a font selector for this site to offer visitors the option of changing the type-face to something they’re more comfortable with. This font selector was made with a PHP Hypertext Preprocessor (PHP) script which, unlike JavaScript, is a server-side utility, meaning users don’t need to have any special add-ons or plugins for their browser to use it. We figured you might like to know how we made it in case you want to employ such a thing on your site.
But Why?
Before we get to actually showing you the script, let’s first discuss why we did it. You see, some people will suggest we don’t specify a font at all, letting the user’s browser preference dictate the type-face shown on the site — assuming the user has a preference and knows how to specify this preference. There’s nothing inherently wrong with letting the user’s preferences rule the day. There is, however, the matter of design and the designer’s own preference. In our eyes, as you probably know, design is an important part of a web site. Our artistic side feels a specific type-face is better than another, thus we specify that font in our style sheet. By doing this, though, we do take this out of the visitor’s hands. To be fair to both the visitor and the designer, a compromise is needed, and offering a scripted function like the one we offer is such a compromise. Here’s how.
How We Did It
The script is fairly straightforward. It’s made in three parts, as follows.
Part One
This part goes above the head of the document — even before the Document Type Declaration (DTD). It must be the first item on the page because the cookie must be set and negotiated before anything else happens.
<?php
// First we define the variables we will use
$var_a= "a";
$var_b= "b";
$var_c= "c";
// Now we define our default “font”
$font = "$var_a";
// We check to see if the cookie has been set and we get the data, if the user has no preference
if(isset( $_COOKIE["cookiename"] ) && !isset( $_GET["cookiename"]) ) {
$font = $_COOKIE["cookiename"];
}
// If the user indicates a preference we set the cookie and redefine the font based on said preference
if(isset( $_GET["cookiename"]) ) {
@setcookie( "cookiename", "".$_GET["cookiename"]."", time()+60*60*24*30 );
$font = "".$_GET["cookiename"]."";
}
// For security, we put the possible variables in an array
$fonts = array( ''.$var_a.'',''.$var_b.'',''.$var_c.'' );
// And if the variable is not what’s been identified, we revert to the default value
if(!in_array($font, $fonts) ) {
$font = $var_a;
}
// The as an added bit of security we sanitize that variable’s content (sort of unnecessary)
$font = stripslashes(strip_tags(trim("$font")));
// Now we negotiate the possible variables and define the values
if($font == "a") {
$font = "100% 'century gothic', futura, sans-serif";
} else if($font == "b") {
$font = "96% 'trebuchet ms', 'lucida grande', sans-serif";
} else if($font == "c") {
$font = "92% verdana, tahoma, sans-serif";
} else {
$font = "100% 'century gothic', futura, sans-serif";
}
?>
Part Two
Now, within the head of the document we need to place the script’s output. In this example we specify the font used for the entire document including form elements. This needs to be placed after the normal screen media style sheet link/import statement. If you need more free downloadable fonts you may just google for “free fonts”.
<!--Based on the data extracted, we output the preference or the default value-->
<style type="text/css" media="screen">
body, input, select, textarea { font : <?php echo(''.$font.''); ?>; }
</style>
Part Three
Lastly we supply the user interface. We placed ours on our sidebar (home page only). You can style this however you like based on your needs. In our case we used a list.
<ul>
<!--Here we offer user the ability to set and define their cookie’s value and set their choice of output (the font)-->
<li><h2 id="fonts">Font Selector</h2>
<ul>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$a.''); ?>">Century</a></li>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$b.''); ?>">Trebuchet</a></li>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$c.''); ?>">Verdana</a></li>
</ul>
</li>
</ul>
Since we’re using WordPress which has a global header, we place Part One in a separate file and “include” it where it needs to be located. The include looks like this:
<?php
include("font-selector.php");
?>
Also, we have limited the interface location to just the home page and, since it’s WordPress, we have to re-include the script file into the file in which the interface exists, like so (this is Part Three, modified):
<?php
if(is_home() && !is_paged()) {
include("font-selector.php");
// The list of links and script include is only added to the “home page”
?>
<ul>
<!--Here we offer user the ability to set and define their cookie’s value and set their choice of output (the font)-->
<li><h2 id="fonts">Font Selector</h2>
<ul>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$a.''); ?>">Century</a></li>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$b.''); ?>">Trebuchet</a></li>
<li><a href="<?php echo(''.htmlentities($_SERVER["PHP_SELF"]).'?cookiename='.$c.''); ?>">Verdana</a></li>
</ul>
</li>
</ul>
<?php
} // Close the conditional output
?>
That’s it. It’s geared towards a WordPress installation, but even if you don’t use WordPress you’ll hopefully you find this useful. This should be a pretty secure script but there’s always room for improvement so if anyone has suggestions, please make them known.
Update: Team Access member Tommy Olsson suggested that for added security, $_SERVER["REQUEST_URI"] be used in lieu of $_SERVER["PHP_SELF"]. Or, perhaps just hard-coding the interface links could be done instead:
<a href="yoursite.com?cookiename=<?php echo(''.$a.''); ?>">Century</a>.
I often make scripts meant for public consumption so I don’t normally hardcode variables but this is good advice. Thanks Tommy.
Another Update: I added the htmlentities() function to the 'PHP_SELF' which also solves the security issue. I changed the main script accordingly in case it’s copied and pasted without reading the note above. htmlentities($_SERVER["PHP_SELF"]) Since this is redundant, though, I’d probably do this another way in practice.

Rich Pedley responds:
Posted: April 3rd, 2007 at 1:58 pm →
Hmmm, I see a few usability problems with this myself. You currently have:
Century, Trebuchet, Helvetica, Verdana, Georgia, Gill Sans, Lucida and Arial in the list for Accessites.
I don’t know about you but I can’t tell by the name what a font actually looks like. Surely the text for the font names should be in the relevant font to make things clearer? Which should be easily enough achieved via a span.
(and apologies I missed the original post about this on Accessites.)
Mike Cherim responds:
Posted: April 3rd, 2007 at 2:33 pm →
That’s a good idea, Rich. I’ll class the links and add those classes to our style sheet. Thanks.
A few minutes later…
Done
Craig Francis responds:
Posted: April 4th, 2007 at 3:22 am →
Its a very good script, just a couple of questions though…
# Why use a GET variable called ‘cookiename’, instead of something like ‘font’?
# What is the ’stripslashes’ etc used for? its only being used on an “if” condition, which should not cause any security problems… if someone tries to add html code into the variable (for an XSS attack), then it will fall back to the safe ‘else’ option.
# In the ‘if’ condition, you could remove the first test (font == ‘a’), as the ‘else’ will catch it, this will also remove the need to use the ‘in_array’ check, as it will also fall back to the ‘else’ default.
# I am a little unsure why there are quotes around the variables, ('’.$var_a.'’)… typically you don’t need to do this ($var_a)… however I have seen a couple of programmers doing this now, so perhaps I’m missing something.
# When you are doing the assignment of the font string, I would personally use a different variable instead of over-writing the $font value, as this helps distinguish the two roles.
Please note that this is not any criticism of your work, it seems to be perfectly good code, and I really like that your using the ‘isset’ function to catch undefined variables.
Best Regards,
Craig
Rich Pedley responds:
Posted: April 4th, 2007 at 4:20 am →
Looks better, though how many people will realise they are in different fonts is another matter.
Mike Cherim responds:
Posted: April 4th, 2007 at 8:20 am →
@Craig: Good questions. Most I can answer. I’m not a good script writer and still consider myself a PHP n00b, but I’ll try.
I figure someone will change this to whatever they want to name their cookie. Think of it as a place holder.
The array check handles the security. It ended up there as a matter of course but it serves no actual purpose in this case.
I suppose the ‘else’ at the end of the script would quiet any errors from undefined variables and that I wouldn’t need to define the variable at the onset. I’d have to go through the think to logic it out again but you’re probably right. I struggle with this stuff.
I’m trying to conform to PEAR standards. I haven’t reviewed them in a while but I think it is there I read that all variables should be quoted. But you’re right in that it’s not necessary. When making stuff for WordPress they request that script writers conform those standards and since I make themes (a one plugin) I’m trying to adopt good practices. Again, though, I really struggle with this stuff. I probably take twice the time needed when writing scripts.
@Rich: True. The developer can only do so much though. The list is headed with “Font Selector” so some of what they need to know can be discovered that way, add to that what you previously suggested and what I implemented as a small visual cue, the rest they can glean through some experimentation. Hopefully.
Craig Francis responds:
Posted: April 5th, 2007 at 3:27 am →
@Mike - All very good comments… it good to see bits of code being shared like this.
I’m just still a little unsure about the comment about ‘all variables should be quoted’.
Using the PEAR sample file, it looks like they don’t use this convention…
http://pear.php.net/manual/en/standards.sample.php
However, that is not to say its not a convention, I just don’t understand the purpose… I wonder if perhaps this might have been taken out of context, as a similar rule is that you should quote all variables which will be sent in an SQL statement. In its briefest form…
mysql_query('DELETE FROM table WHERE id="' . mysql_real_escape_string($_GET['id']) . '"');In this (far from perfect) example, the escape string function stops the ID from being set to…
http://example.com/?id=1%22%20OR%20%22%22%20=%20%22As after removing the URL encoding and adding it to the above SQL without the quoting, this will produce
DELETE FROM table WHERE id="1" OR "" = "";This will effectively delete every record in the table… on the other hand, if the quotes were used, then it will form:
DELETE FROM table WHERE id="1\" OR \"\" = \"";Which should not delete any records, taking that the ID is an integer.
Hopefully that describes the basis of SQL injection, which can get messy when you start talking about using.
However, most of this is irrelevant… its still good work… well done!
Mike Cherim responds:
Posted: April 5th, 2007 at 6:53 am →
@Craig: I appreciate your comments and the encouragement. Regarding quoting variables…
That is quite likely knowing me, I do and learn, more than read and learn.
I tend to overkill PHP to death in the process of my learning. Also because I don’t know how all exploits work or where all the loopholes are I end up doing too much (more than necessary) to code in general. I get something to perform smoothly and consistently, then I apply as much to it as I can to define (or confine) its usage to what’s intended (in this latest experiment at another site I have — my playground — users can generate up to 28 user errors). So much to learn. I get a lot from posting scripts and getting user feedback, actually. Makes me reevaluate my practices. There’s so much I don’t know.
I didn’t, for instance, know that
PHP_SELFwasn’t safe to use and that it should be treated as user input. Tommy woke me up, and Mel found this great article about it. Based on the article (haven’t read all the comments yet), it looks likehtmlentities($_SERVER['PHP_SELF']);solves that problem, or at least nullifies any possible payloads.Craig Francis responds:
Posted: April 6th, 2007 at 3:28 am →
@Mike To be honest, your overkill method is the one I tend to prefer… its better to cover as many potential security holes as possible, than to only do ‘just enough’, or in other words ‘just about works’.
As a general rule, you should not trust any variables your script has not created (user input, server variables, values from the database, etc)… which you seem to have covered quite well… but its even questionable if you should even trust the variables your code has created (who knows who will edit the script in the future and put un-safe data in that variable).
So all my variables go though mysql_escape_string() before being sent to the database, even those which I have just defined myself… anything that goes in a URL, goes though urlencode()… anything that gets printed to the page goes though htmlentities()… anything that is used in a perl regular expression goes though preg_quote()…
Also, anything which is used to open a file is cleaned using a regular expression like ’s/[^a-z0-9]//i’, as this stops a variable being set like:
$_GET['path'] = '../../../../etc/passwd';$myPath = preg_replace('/[^a-z0-9]/i', '', $_GET['path']);fpassthru('/www/files/' . $myPath);I find it handy to use a variable prefix to help define the role of the variable… for example, if I have a variable that needs to contain a link, then I cannot pass it though htmlentities()… so to mark that variable as holding ‘HTML safe data’, its prefixed with $html… for example $htmlError… any variables which are passed into that variable MUST go though htmlentities()… then I know its contents are safe to be printed out onto the page… for example:
$htmlError = 'This article already exists (view)’;echo $htmlError;Its the same with SQL… if I’m building up a dynamic WHERE clause, I usually create it using an $sqlWhere variable.
Mike Cherim responds:
Posted: April 6th, 2007 at 2:22 pm →
@Craig: Good to know I’m not the only person who tries to take nothing for granted when it comes to security. It’s scary out there. One look at error and access logs will put the fear in anyone I suppose. Miscreants come-a-knockin’ at the door daily.
Craig Francis responds:
Posted: April 7th, 2007 at 3:02 am →
@Mike: If your up for the challenge, I suggest you get your servers error log emailed to you on a weekly basis (logrotate)… and if you have one, the PHP error log… then try to use Aliases to clean out the usual scanning requests (/MSOffice/cltreq.asp) and if necessary, some RewriteRule’s for the old URL’s.
Once you start getting clean-ish error logs, then it provides another source to check for broken links, etc.
NOTE: For PHP on a live server, set:
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /var/log/php.log
Mike Cherim responds:
Posted: April 7th, 2007 at 9:34 am →
I do check my logs often, and I do make some changes in response, but I don’t have, nor can I stick to, a formal regimen. But I check ‘em out about once a week to see what’s up. As far as I know I don’t really have any broken links except null links in my portfolio. I try hard to make sure I stay current. Sanctity of the web and all that
Best of March 2007 | Smashing Magazine responds:
Posted: April 11th, 2007 at 11:05 am →
[…] Making a Font Selector “We recently made a font selector for this site to offer visitors the option of changing the type-face to something they’re more comfortable with. This font selector was made with a PHP Hypertext Preprocessor (PHP) script which, unlike JavaScript, is a server-side utility, meaning users don’t need to have any special add-ons or plugins for their browser to use it.” […]
Webdesign (css, grafica e altro) » Blog Archive » Best of March 2007 responds:
Posted: April 18th, 2007 at 11:43 am →
[…] Making a Font Selector “We recently made a font selector for this site to offer visitors the option of changing the type-face to something they’re more comfortable with. This font selector was made with a PHP Hypertext Preprocessor (PHP) script which, unlike JavaScript, is a server-side utility, meaning users don’t need to have any special add-ons or plugins for their browser to use it.” […]
Marie ALHOMME responds:
Posted: May 8th, 2007 at 2:06 pm →
Hello !
I just LOVED the article, so I translated it to French, it’s available at the following url :
french translation.
Hope you find it useful !
Marie
–
Bonjour !
J’ai ADOR2 cet article et l’ai donc traduit en Français, disponible à l’adresse suivante :
traduction française.
J’espère qu’elle vous rendra service !
Marie