87 Reader Comments

> setcookie (‘sitestyle’, $set, time()+31536000, ‘/’, ‘yourdomain.com’, ‘0’);
If $set is to contain the HTTP GET variable from the query string, use
$_GET[‘set’]. This is the preferred way of working with client input from the query string. Old style PHP works fine as the example is, but newer versions probably have register_clobals off, meaning input is separated:
$_GET
$_POST
$_COOKIE
$_REQUEST (all of the above)
$_FILES (uploaded files using POST)
$_SERVER (user AND server variables, e.g. HTTP headers, environment variables)

Here’s some nitty gritty from the PHP documentation:
>In PHP 4.2.0 and later, the default set of predefined variables which
>are available in the global scope has changed. Individual input and
>server variables are by default no longer placed directly into the global
>scope; rather, they are placed into the following superglobal arrays.

Apart from that, of course, it’s a nice article. Good to see ALA going more developer-ish again. :-)

A suggestion:
Instead of putting the PHP to select which stylesheet to use in every page, wouldn’t it make more sense to do something like this:
<link rel=“stylesheet” type=“text/css” media=“screen” title=“User
Defined Style” href=“styleredirect.php” /> and then have styleredirect.php redirect to the appropriate stylesheet? That way, you could have one copy of the code instead of scattering it throughout every page of your site. It would make maintenence much simpler. Unfortunately, I don’t know PHP, so I’m not entirely sure how to actually code this.

This script has a potential security flaw - an HTML injection attack. The script embeds HTML in to the document directly from the query string of the page without first checking that the HTML is “safe”. This could allow a crafty cracker to steal your browser’s cookies for the site, which could lead to a security breach if cookies are used to store administrator sessions (for a content management system for example). As a lesser evil, it could also let malicious third parties to add inapropriate content to your site as viewed by another user.

Here’s (roughly) how it would work. A cracker tricks someone in to visiting your site with a URL that looks like this:

The included script file has code which uses javascript to grab any available cookies for that domain and silently send them off to a script running on the hacker’s site (through a invisible HTTP GET request, probably by including another script or an image) which logs your cookies. The same technique could be used to add HTML to the page, which could allow people to “add” pornography to your site. The added HTML would only be visible to users who visited the site via the “fake” link provided by the cracker but could still lead to confusion and, in the case of administrator cookies being stolen, a full scale security breach.

One drawback of this technique is that if there are any websites’ pages in the browser’s history AFTER your website’s page, they’re just *poof* gone when the switcher link is clicked.
(Then again, that was the case with last year’s browsers, maybe by now they implemented a check for 0 bytes pages not to be included in the history.)

Marty, the most this security breach (that the sanitised version of the code avoids) could do is get the cookies from the website the PHP switched is on. However, this is only if using javascript. If you passed some PHP code in the querystring value, you could do much more than that.

There really aren’t security issues with PHP itself, it’s only that’s it’s very easy for unexperienced people to produce insecure code.
Eventually, it all comes down to the good “never trust the user” doctrine, and the systematic sanitisation of user-submitted data. Once you start following these simple rules, your PHP will be as secure as can be. :)

The security breach I described is only a significant problem if you run a site that has a cookie-powered login for an administration panel - as is the case with most web based content management systems. Sites that do not use cookies in this way are not nearly as badly affected - a malicious third party could still “trick” someone by adding content to your site temporarily (through adding it to the query string and tricking someone in to visiting the site via that URL). Michel’s sanitiser is a good way of avoiding this - I tend to use PHP’s htmlentities() function for the same purpose.

Cheers, that’s a helpful article, but one little meta-gripe. It’d be nice if i could make the small text larger in ala articles by choosing View -> Text Size -> Larger [IE6/Win2002]. Jakob N would roll in his grave (if he were dead).

OK… No, I haven’t used PHP before… and this seemed like such a perfect idea since I have a client going live in two days thought wants accessibility and styleswitching. I was attempting to use this as a “no javascript” method of doing this.

I contacted my host who said that PHP was now enabled for the site… followed the directions in the tut… and ended up with a completely unstyled page. :( When trying it in Mozilla, I do see the option of “other stylesheets” and can even choose the one I have as an alternate… but not with the PHP switcher…

Has anyone been successful with this article? Is it missing anything? I even tried the code in Michel V’s post above and now I’m getting an error that says:

Warning: Cannot add header information - headers already sent by (output started at /home/sites/site171/web/testing/switcher.php:2) in /home/sites/site171/web/testing/switcher.php on line 5

Warning: Cannot add header information - headers already sent by (output started at /home/sites/site171/web/testing/switcher.php:2) in /home/sites/site171/web/testing/switcher.php on line 6

So is it hopeless? Or is there some little errata… or am I just “not getting it?” It has been a long day. :-P

As was mentioned, the variable $set can be made to be anything (including bogus HTML) and that information would then be saved to the clients machine. In circumstances beyond what was discussed above, I’m not sure that this is a big deal, but you’re allowing external sites to set cookies for your site this way.

One thing that immediately popped to mind is that you should check the referer. There could be problems with proxy servers (as was mentioned), but if you check the referer to see if it is YOUR server, then you are better off. Also you may check the value of $site against some “acceptable” names like “red”, “blue”, “green” like this:

I think it’s kind of neat to see code in the article almost identical to what I’ve done before with this. Great minds, right? In fact it gave me reason to dig out the script of the site it’s on and clean it up a bit, incorporating all the suggestions made thus far as well as more modern PHP coding techniques. Until I saw it here:
http://www.contrastsweb.com/switcher/
Again, it’s eerie how similar the code is. I’m going to start reading Rob’s blog because it’s nice to find someone who thinks similarly. I would recommend the code above, with only two minor changes based on experience I’ve had.

First, Netscape (bah!) has trouble with cookies whose domain has less than two periods in it. The nice way to fix this is to write your domain like “.example.com” (note the leading period!) which also has the nice side effect of making the cookie valid for any sub-domains you may have. Even if you don’t use sub-domains, this allows the same cookie to be used for people accessing your site through either yourdomain.com or www.yourdomain.com.

I’ve also had trouble with PHP’s built-in setcookie function, so if that’s giving you trouble, you can always fall back on using straight headers to take care of things. Such as:
header(“Set-Cookie: theme=$theme; path=/; domain=.photomatt.net; expires=”.gmstrftime(”%A, %d-%b-%Y %H:%M:%S GMT”, time()+10960000));

For more information on cookies in general, and for a little trip down memory lane, check out the original cookie specification from Netscape at http://wp.netscape.com/newsref/std/cookie_spec.html. Partly I think because of their sadly lacking version 4 release, people forget how much Netscape really expanded technology behind the web. Sometimes for better, sometimes for worse. On that same vein it’s not a flaw in PHP that allows things like injection attacks, but rather coding that doesn’t consider as many possibilities as possible. I’ve found that coding for security in turn makes my code cleaner, more maintainable, and less likely to have to be rewritten after a while. Think of it as forward compatibility on the backend :)

Meta: Could someone take a look at the regex (or whatever) is parsing the URLs for the forums? It works fine with most everything, but chokes on a few notable common URL characters.

Arg, it ate the period in the URL I posted above, here’s a clean version:
http://wp.netscape.com/newsref/std/cookie_spec.html
That’s really my fault though; I should have put a space after the URL. Maybe it would just be easier to allow *just* anchor tags. The web is all about links. Sanitizing for one tag should be fairly trivial as well. Another solution would be the option to preview a post, so you can tell how things are going to turn out.

One solution to the problem with using $_SERVER[‘HTTP_REFERER’] would be to send the current URL to switcher.php along with the stylesheet like this:

switcher.php?set=red&ref;=<?=$_SERVER[‘PHP_SELF’]?>

Then use $ref instead of $_SERVER[‘HTTP_REFERER’].

Stephanie: You get those errors when you try to call the header() function after some html has been sent to the browser. Make sure that you don’t have any content (even a single space) before your opening <?php tag. Good luck.

Alan, that’s just to ensure the script gets data that it could still use. If someone uses the evil.js method that Simon showed, strip_tags() will still put a remaining closing angle bracket as a cookie, and that can break the HTML that the page produces.
Just removing anything that’s not alphanumeric ensures that the cookie set in the evil way isn’t going to break the HTML :)

OK, this is probably showing some ignorance on my part, but doesn’t this method (stage two, detecting styles) require all pages using the switcher to be parsed by php? A <?php echo (!$sitestyle)?‘defaultstyle’:$sitestyle ?> in a “link” tag isn’t going to be executed in a plain HTML page unless the server is configured to have PHP parse .html as well as .php, right? And mod_perl pages and other things with their own handlers wouldn’t do anything with PHP instructions either. Am I missing something?

You aren’t missing anything. The PHP snippet to echo the stylesheet name requires the page to be parsed as PHP.
But it’s possible to achieve the same effect with [removed] read a cookie named ‘stylesheet’, if it doesn’t exist, make stylesheet = a default string. “This is left as an exercise for the reader.” :)

There is more chance that a user will have cookies disabled than JavaScript disabled. All the above will not work without cookies.
I am using SESSIONS in my version, this script assumes that PHP 4.2+ is installed and that register_globals is off with—enable-trans-sid.

As an extra, for future visits, you could of course set a cookie as well, but this method would not need cookies or JavaScript to work during the users SESSION. If cookies were disabled, then the SID would be passed with the URL, or you could even code the SID in if enable-trans-sid is not compiled on your server.

The person who suggested PHP be put in the CSS document was real close - you can’t put PHP inside a .css file, since .css isn’t parsed by PHP unless you set it to, so here’s an easier trick:

Use a CSS document name of <file>.php - the file will (haven’t tested this, but it makes sense) be parsed by PHP before being shipped off to the client when they request it. As long as you make sure to define it as text/css, the browser shouldn’t care about it’s extension.

In the <file>.php file, just put all the code you need to do your dirty work. Oh and a tip for this… don’t use cookies with long expiry dates - set the expiry time to 0, which will make it die as soon as the browser closes (at the end of a “session”). Also, as mentioned before, don’t let people set whatever they want :) Whenever you accept user input, you have to take every precaution imaginable to prevent someone mis-using your scripts.

One week after I’d finished my own version of this it appears here. Never mind.

My version is different in that it doesn’t use GET variables to set the cookie. At the moment my switch-style links point to locations of the form: /ui/configure/“style-to-apply”/. Whilst this means that at the moment I am having to put code in each subdirectory it does mean that the only way to do an HTML insertion attack is to change the actual value of the cookie.

Whilst this is not the best solution to the problem it avoid the injection attack. One way to make the code easier to maintain would be to use the techniques from http://www.alistapart.com/stories/succeed/ the mod_rewrite article to redirect requests to the subdirectory to a script that could encode the correct cookie.

>>>>>>
In the <file>.php file, just put all the code you need to do your dirty work. Oh and a tip for this… don’t use cookies with long expiry dates - set the expiry time to 0, which will make it die as soon as the browser closes (at the end of a “session”).
>>>>>>

what would be the point of using a cookie then if you’re setting it to 0? If that’s the case then you should be using a session variable instead.

What’s the point of having ‘save’ on the file menu? It gives you different ways of doing the same thing.

Also, regarding the suggestion that, without checking, a malicious user could run their own PHP code: not possible. For this to happen, the script must contain an eval() statement or something that uses unchecked user input.

Is it possible to have the currently selected choice not linked -
meaning, if i’m at red there is no reason for me to be able to
click on “red.” the text could still be there as an indicator
of the current choice. I just thought of that, but have no idea how to do it in PHP or if it could be done, but if it could that’d be pretty cool.

There is no easy way (that I know of) to de-link the current link. The non-easy way is to generate you links for each page. Include a seperate file in each page that contains you navbar code. When you want to generate the style links then something along these lines would work (pseudo-code):

Using <link href=“stylesheet.php” ...>
gets around the requirement of having every page on the website parsed by php. Only 2 pages need to be parsed, instead. Just be sure to include:
header(“content-type:text/css”);
in the php script before you output anything else.

One suggestion for getting around relying on the $HTTP_REFERRER tag is to use a javascript to call the switch.php?set=red script, as the src for a hidden image.
<a href=”[removed]hiddenImage.src=‘switch.php?set=red’;reload()”> or some such typing (I’m not very good with JS). Or, make the image non-hidden, and actually have the stylesheet.php return an image based on the selection. Could have some fun with this.

I was trying to find an alternative to javascript not cos of people having it disabled, but because of a conflict between something I had just implemented and the window.onload of the old ALA switcher ...

I’ve remodelled my site in PHP recently, so this was the next logical step!

Thanks for the session-level piece-of-code ... that makes my site just perfect!

Boyd: Maybe not, but you could tell for sure by adding three lines of code to switcher.php. This would also have the advantage of showing which URL and style people preferred. In fact it could even catch any would-be hackers so you could ban them. If you wanted you could also add browser, host, and time.

Add that, then create a log.txt and chmod it appropriately. True, it’d be relatively slow and byte intensive but you could use it for a short period of time just to measure user responce. As for a MySQL version, I’ll leave that as an excercise for the reader… ;)

First if you are already parsing pages to add the styles then there’s no reason not to put the cookie code right into the page itself instead of using another (switcher) page.
(I know about the new _GET var but I’m not using it quite yet in shared code because not everyone has upgraded their php yet)

simple enough no extra pages and bouncing around. Someone else has already mentioned the inheirent security issues. This hack has been availible in almost every PHP tutorial on this site. If you are going to write server side coding tutes please know some basic security concerns before opening newbies up for attacks.

Ok, just about got this to work (website in development stage, still!). What I would like to do, and could with javascript using the styleswitcher in ALA 126, was to display the name of the style in use, and parse the filename to a link enabling the user to validate the stylesheet in use via W3C’s validator. (didn’t work in Opera tho. huh!)

This error:
Warning: Cannot add header information - headers already sent by (output started at /home/sites/site171/web/testing/switcher.php:2) in /home/sites/site171/web/testing/switcher.php on line 5

Is often caused by having whitespace at the beginning or end of your php file. So take out any whitespace (tabs, spaces, newlines) from the PHP file, and that *might* fix it. The general idea here is that *nothing* can be output before calling the header() function (not even whitespace).

For more info see the PHP manual page on the header() function:
http://www.php.net/manual/en/function.header.php

I am also new to php so, please bear with me…
I would like to use this php code to enable a style switcher on my site. My purpose for a style switcher is not for the user to pick a new style bc in my humble opinion most users dont really care. I would like to use it for styles to switch when different browsers are used to view the page. So, if a user opens a page with ie the php code recognizes that the browser is IE and loads the page with the title “ie” or “ns” or “opera” in the link rel tag.

It should be obvious what I’m trying to achieve (two adjacent span tags with metric and US customary units), however when NS disables both alternates and then enables the desired alternate both <SPAN> tags fail to display, rather than the ‘switching’ behaviour desired (and working in IE).

I have something just like this, using sessions and PHP, but I also have control-blocks scattered at intervals in the source-framework to do special modifies at special designs, not just CSS.

Try switching from one design to another, and view source for all of them; they’re kinda the same, but also not quite, depending on the design. The writing of these extra style-HTML is very easy, and stored away in separate files and functions.

But it sure will take a lot out of your servers resources when many come alookin’, though. Beware.

The problem with using php in a css file is that you lose the benefit of the client’s browser caching the css file after a single access. Now that the css file is dynamic, the browser will have to download it again each time a page is viewed. So you may want to keep your core css in a static cacheable .css file and the few scripted parts in a small .css.php file. This saves bandwidth for everyone, woo!

I would’ve jumped in earlier, except that I don’t read here very often. My bad.

I’ve had a tutorial up on PHP + skinning for over a year here: http://www.domesticat.net/skins/howto.php

It’s interesting to see that other people are interested in this particular bandwagon.

One thing that I did differently than everyone here, it seems, is make each skin’s name numeric. (I currently have seventeen.) Keeping the names numeric means that I can run a very specific set of checks on the requested value of $skin (is it a positive integer? is it within the range of numbers I specify?) and drop the user back to a default skin if their requested value of $skin is invalid.

In some ways what I’m doing goes a bit beyond the scope of this discussion; I’m using the value of $skin to do more than just swap a stylesheet. There are a lot of extras available on my site that are just a bit too much for those using modems, lynx, or handhelds, so there are superslim versions of the site geared toward those devices.

Most of the tutorial has more to do with preparing your site for skinning purposes, and most of it’s utter common sense. Perhaps it’ll help someone in the future.

I know this forum has been dead for a while but hopefully one of you PHP heads is still checking it out. I use an SSI tag to include a navbar, and I was able to do that in my index.php file using a php include statement. (because apparently regular SSI tags won;t work here). THe problem is, none of the css styles get applied to the included navbar, which I tried as an html, a php, and I even tried adding the stylesheet select code that goes in every page to no avail. I hope someone can help; I’m not sure how to fix this.

The CSS switcher DOES affect these links as they appear on index.php through a browser, but the applied CSS style does NOT affect the pipes (|) that are in between the links here. I know it seems trivial, but not only do I want the pipes to appear correct, eventually I may add more to the nav bar, like a table to hold the links, or other non-link text. Please help

this sounds like the problem I ran into some time ago. The separator lines are not affected by the <a> style as they are not part of the link. Instead you might want to wrap the navbar into a <div id=“navbar”> and define your style attributes for…

I noticed that without cookies enabled this whole thing fails to work. I guess this was obvious but I had originally thought that setting the cookie was simply so that the user came back to his or her chosen style. Is there a workaround that allows the switcher to still work if cookies are disabled or is that just how it is?

I *HAD* it all working on my blog (MT on Apache2/PHP4.3.0) and after one day and a couple of index-rebuilds it was all gone… :? The switcher.php wasn’t working (anymore): the browser kept on that page and didn’t return to index.php with a new stylesheet.
Now I see a lot of suggestions here but I miss a complete solution e.g. not just rewrites of some possible buggy rule in switcher.php :)
Anyone PLEASE? TIA!

I wish that there was a way of doing this using ASP. I know, I know, PHP is better, faster, etc. The problem is, my ISP doesn’t have PHP installed, and only supports ASP. The website I’m working on (see URL) is entirely authored in standards-compliant xhtml and css, and I just wish I could take advantage of that.

If this works, and I guess it does, then why doesn’t AListApart use this in addition to the Javascript solution. If they used this as well (instead of an empty response on the buttons) then Opera users could switch styles as well…

i tried this a short while ago, and it didn’t work for me in netscape. to get around it i tried adding AddType application/x-httpd-php .css to .htaccess, and keeping the .css extension, which for some reason also didn’t work in netscape, although IE was fine (never tested opera). never did find out why =\.

I’m using this on my blog website http://www.slowchildrenatplay.org/ and it works fine in IE 5.5, IE 6 and Netscape 6.2. When I try to change the styles in Netscape 7 though, I have some trouble. When I click one of the style-changing links, the browsers goes to http://www.slowchildrenatplay.org/switcher.php?set=style but doesn’t refresh… it stays there, looking blank. If I hit the back button and refresh the index page, the new style is applied. Does anyone know how this switcher works with NN 7? Can someone look at my code and see if I made a mistake? (I’ve double checked a few times, and everything appears to be in order.)

Thank you for putting up a complete example for a novice like me! i spent 12 hours straight playing with this idea and it wasnt till i used your example that i got everything to work beautifully! THANX!

Hi there, I’m trying to use this code to switch styles on my website. I have used ALA’s Javascript switcher before, and it worked. But I can’t get the PHP version to work! I followed the tutorial step by step, and checked everything I could. It just shows plain text without any stylesheet (even if I let it show the default one). My server runs PHP and other scripts work perfectly.

here is more chance that a user will have cookies disabled than JavaScript disabled. All the above will not work without cookies.
I am using SESSIONS in my version, this script assumes that PHP 4.2+ is installed and that register_globals is off with—enable-trans-sid.