Supporting Legacy Browsers, or Not
Following a conversation about the problems of supplying accessible content to old (legacy) browsers Mike Cherim came up with a PHP solution that would allow developers to serve very plain content to legacy browsers but rich content to everyone else.
There’s nothing wrong with Mike’s code but I felt it could be tidied up and, possibly, tweaked to give faster performance. After all, if you’re going to use this on a big, high traffic site, performance could become an issue.
Mike’s approach uses eregi which I knew was probably the slowest of the usable PHP matching functions. According to php.net, it’s outperformed by preg_match and stristr but the fastest possible comparison, if you just want to identify if one string is present in another, is strpos. There’s also stripos for a fast case-insensitive match if you happen to use PHP 5 but, for now, I think it’s better to avoid PHP 5 specific functions.
Secondly, Mike’s script used a whole stack of OR operators to actually carry out the string comparison. Now, I’m probably being purist but I’ve always thought that if you have more than two or three repetitions of AND or OR in an if statement, it might be an idea to look for something a little more elegant and possibly faster. Added to which, I find them difficult to read!
I also, personally, dislike burying lists of variable assignments within the code itself. If there’s a chance I’ll want to re-configure the variables, I want them where I can edit them easily and where amending them is likely to do the least damage (I’m a lousy typist), So setting up an array to hold the legacy browser identifiers seemed to be in order.
Finally, I wasn’t sure how the script would behave if the user agent header wasn’t received. Would it throw up warnings? Should a situation where where the headers were blocked be considered? If so, what action should the script take? In the end, I decided that the script should cope with this scenario and, if the user agent wasn’t known, it should deliver content with minimal styling – ‘just in case’.
As it turned out, I only made changes to the core script, browser_list.php.
<?php
// Secure the include
if( $ping != "pong" ) exit( '<h2>You cannot access this file directly!</h2>' );
// the array to hold our list of legacy browsers
// all entries need to be lowercase otherwise we'll have to incorporate another strtolower() call
$oldbrowsers = array(
"unknown",
"mozilla/4.78",
"netscape6/6.2.3",
"netscape/7.2",
"msie 3",
"msie 4.01",
"msie 5.01",
"msie 5.23",
"msie 5.5",
"opera 7.23",
"opera 7.54",
"opera 7.54ul"
);
// set up the browser type variable.
$browser_type = "";
// Account for blocked or unsent headers
if(!empty($_SERVER['HTTP_USER_AGENT'])) $users_browser = strtolower($_SERVER['HTTP_USER_AGENT']);
else $users_browser = "unknown";
// Test to see if the user's browser is in the legacy browsers array
foreach($oldbrowsers as $entry) {
if(strpos($users_browser,$entry) !== false) $browser_type = "legacy";
}
?>
Inside the header of each page, we still have:
<?php
$ping = "pong";
include_once("browser_list.php");
if( $browser_type == "legacy" ) echo( '<link rel="stylesheet" href="basic.css" type="text/css" media="screen" />' );
else echo( '<style type="text/css" media="screen"> @import "complex.css"; </style>' );
?>
The only other thing you need is your two CSS sheets – basic.css for the legacy browsers and complex.css for everyone else.
Apart from hopefully being a little faster than Mike’s orginal, the only difference is that, if the user agent header isn’t received, the script serves the minimally styled content. If that’s not the behaviour you want, simply remove ‘unknown’ from the $oldbrowsers array.