Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Serving up CSS images from outside document root 1

Status
Not open for further replies.

OsakaWebbie

Programmer
Feb 11, 2003
628
JP
From a single document root (where all my PHP files are located for a database interface), I have set up code to access client-specific files from another directory (/var/ One of the types of files I get from there is CSS, so that each client can have their own colors and such. Getting the CSS files is no problem - I have:
Code:
<link rel="stylesheet" href="style.php" type="text/css" />
And in style.php (after security checks) I have:
Code:
$path = "/var/www/".$_SESSION['client']."/css/";
header("Content-type: text/css");
readfile($path."style.css");
The problem is how to get images that are specified in the CSS, like background-image. Client-specific images referenced in HTML can be served in a similar way to the CSS files, like:
Code:
<img src="getphoto.php?file=whatever">
{in getphoto.php}
$path = "/var/www/".$_SESSION['client']."/photos/";
header("Content-type: image/jpg");
readfile($path.$_GET['file']);
But that doesn't work in CSS as far as I know. I tried:
Code:
background-image: url(cssimage.php?file=whatever);
{similar code in cssimage.php with a different path and detecting the mime type}
But unless I just have a bug in my code, sticking a PHP file reference in CSS doesn't work. Does anyone know how I can do this?
 
One way, would be to include or require your CSS file rather than reading it. Including or requiring makes the parser go through the file and parse the Php in it.

Personally, I would add a path variable to be used in the CSS for the images and such, one that can be dynamically built based on the client so the path points to the correct image folder.

----------------------------------
Phil AKA Vacunita
----------------------------------
Ignorance is not necessarily Bliss, case in point:
Unknown has caused an Unknown Error on Unknown and must be shutdown to prevent damage to Unknown.

Web & Tech
 
Hi

OsakaWebbie said:
But unless I just have a bug in my code, sticking a PHP file reference in CSS doesn't work.
I just put together a test from the code pieces you posted and it works. Actually there is no reason to not work. So try debugging it step by step. ( I suppose access to a page manifesting that problem is not possible. )


Feherke.
 
like feherke I'd say it should work. perhaps enclose the uri in quotes - some browsers may be picky?
 
Thanks, everyone! I didn't totally understand what vacunita was suggesting, as include/require would make the file part of PHP rather than requested by the browser as part of a <link>. (Some of the CSS files are third-party generated, like jquery-ui.css, so I don't want to have to do major editing to them all.) But after reading that feherke had success with my idea, I tried harder to troubleshoot. (The layers of file calls are such that visibility of what is going on is pretty tricky.)

It turns out that my "bug" was really silly, not actually a bug at all - not watching the status area of Filezilla when saving the edited CSS file. The file owners of my main files and my client files are different (I knew that, but wasn't thinking), so the saves weren't actually happening! [blush]

Of course, editing the CSS files directly was only a testing step - now that I know it works, here is my final solution, which does everything on the fly (I also made clientfile.php generic enough to use for images elsewhere):
Code:
[red]style.php:[/red]
...security stuff...
$path = "/var/www/".$_SESSION['client']."/css/";
header("Content-type: text/css");
serve($path."reset.css");
serve($path."styles.css");
...more like that...
function serve($source) {
  $stuff = file_get_contents($source);
  echo preg_replace('#url\( *["\']?([^"\'\)]*)["\']? *\)#', 'url("clientfile.php?f=css/$1")', $stuff);
}

[red]clientfile.php:[/red]
...security stuff...
$mime_map = array(
  'gif' => 'image/gif',
  'ico' => 'image/x-ico',
  'jpg' => 'image/jpeg',
  'png' => 'image/png',
);
$fullpath = "/var/www/".$_SESSION['client']."/".$_GET['f'];
if(!is_file($fullpath)) die("Bad path: ".$fullpath);
header("Content-type: ".$mime_map[strtolower(pathinfo($_GET['f'], PATHINFO_EXTENSION))]);
readfile($fullpath);
 
Hi

Glad to see you solved it.

One thing : those files will rarely change, so most of the time you could just tell the browsers to reuse their cached copy. You only need to
[ul]
[li]send [tt]Last-Modified[/tt] HTTP response header for each file[/li]
[li]check the [tt]If-Modified-Since[/tt] HTTP request header and send 304 HTTP status code if nothing changed[/li]
[/ul]
Code:
[teal]<?php[/teal]
[gray]//...security stuff...[/gray]
[navy]$mime_map[/navy] [teal]=[/teal] [b]array[/b][teal]([/teal]
  [green][i]'gif'[/i][/green] [teal]=>[/teal] [green][i]'image/gif'[/i][/green][teal],[/teal]
  [green][i]'ico'[/i][/green] [teal]=>[/teal] [green][i]'image/x-ico'[/i][/green][teal],[/teal]
  [green][i]'jpg'[/i][/green] [teal]=>[/teal] [green][i]'image/jpeg'[/i][/green][teal],[/teal]
  [green][i]'png'[/i][/green] [teal]=>[/teal] [green][i]'image/png'[/i][/green][teal],[/teal]
[teal]);[/teal]
[navy]$fullpath[/navy] [teal]=[/teal] [green][i]"/var/www/"[/i][/green][teal].[/teal][navy]$_SESSION[/navy][teal][[/teal][green][i]'client'[/i][/green][teal]].[/teal][green][i]"/"[/i][/green][teal].[/teal][navy]$_GET[/navy][teal][[/teal][green][i]'f'[/i][/green][teal]];[/teal]
[b]if[/b] [teal](![/teal][COLOR=darkgoldenrod]is_file[/color][teal]([/teal][navy]$fullpath[/navy][teal]))[/teal] [b]die[/b][teal]([/teal][green][i]"Bad path: "[/i][/green][teal].[/teal][navy]$fullpath[/navy][teal]);[/teal]
[highlight][navy]$modified[/navy][teal]=[/teal][COLOR=darkgoldenrod]gmdate[/color][teal]([/teal][green][i]'D, d M Y H:i:s'[/i][/green][teal],[/teal][COLOR=darkgoldenrod]filemtime[/color][teal]([/teal][navy]$fullpath[/navy][teal])).[/teal][green][i]' GMT'[/i][/green][teal];[/teal][/highlight]
[highlight][b]if[/b] [teal]([/teal][navy]$_SERVER[/navy][teal][[/teal][green][i]'HTTP_IF_MODIFIED_SINCE'[/i][/green][teal]][/teal] [teal]&&[/teal] [navy]$_SERVER[/navy][teal][[/teal][green][i]'HTTP_IF_MODIFIED_SINCE'[/i][/green][teal]]==[/teal][navy]$modified[/navy][teal])[/teal] [teal]{[/teal][/highlight]
  [highlight][COLOR=darkgoldenrod]header[/color][teal]([/teal][green][i]'HTTP/1.1 304 Not Modified'[/i][/green][teal]);[/teal][/highlight]
  [highlight][b]exit[/b][teal];[/teal][/highlight]
[highlight][teal]}[/teal][/highlight]
[COLOR=darkgoldenrod]header[/color][teal]([/teal][green][i]"Content-type: "[/i][/green][teal].[/teal][navy]$mime_map[/navy][teal][[/teal][COLOR=darkgoldenrod]strtolower[/color][teal]([/teal][COLOR=darkgoldenrod]pathinfo[/color][teal]([/teal][navy]$_GET[/navy][teal][[/teal][green][i]'f'[/i][/green][teal]],[/teal] PATHINFO_EXTENSION[teal]))]);[/teal]
[highlight][COLOR=darkgoldenrod]header[/color][teal]([/teal][green][i]"Last-Modified: $modified"[/i][/green][teal]);[/teal][/highlight]
[COLOR=darkgoldenrod]readfile[/color][teal]([/teal][navy]$fullpath[/navy][teal]);[/teal]


Feherke.
 
Nice thought! I'm definitely in favor of maximizing use of cache - I'll give it a try. Thanks!

P.S. How did you get the TGML "code" box to include the filename at the top? I wanted to do that in my previous post (separating the content into two code boxes), but there was no mention in the TGML cheat-sheet.

It also looks like you have some sort of TGML generator in your code editor - I doubt you did all that color-coding by hand... ;-)
 
Hi

OsakaWebbie said:
How did you get the TGML "code" box to include the filename at the top?
Yepp, initially no parameter was supported for the [tt][ignore]
Code:
[/ignore][/tt] tag and seems the documentation was not updated.

So you write

[tt][ignore][code=sample TGML]
blah-blah
[/ignore][/tt]

And you get

Code:
blah-blah
OsakaWebbie said:
It also looks like you have some sort of TGML generator in your code editor - I doubt you did all that color-coding by hand... ;-)
Indeed. I use GNU Source-highlight. It is very flexible, writing an output language definition file for TGML takes just a couple of minutes. Pygments could also be used as it also extensible and supports custom output formats.

And to avoid copying and pasting, I use It's All Text! to open the [tt]textarea[/tt]'s content in an editor. In my case MCEdit, which is able to pass the selected text to an external command then replace it with the command's result.

Feherke.
 
as an alternative to feherke's two classes, i wrote a script that mark-ups code with tgml tags.

to use it, attach the following to a bookmark and then just click the bookmark (once) on any post page. it's a little crude but works fine.

Code:
javascript:(function(){var%20c=jQuery('#postform%20textarea');if(!c.length||c.val().indexOf('[code')==-1)return;jQuery.post('[URL unfurl="true"]http://tgml.rathercurious.net',{language:prompt('Which%20language%20is%20your%20code%20in%20(this%20will%20be%20used%20as%20a%20default%20if%20you%20have%20not%20included%20a%20lang={lang}%20in%20the%20code%20tag)',''),code:c.val(),%20requestType:'cors'},function(data){if(data.result=='ok')c.val(data.html)},'json')})();void(0)[/URL]
 
You guys have all kinds of tricks!

jpadie, I think you are selling yourself short on the Tektips user category (or whatever that's called that is in parentheses after our handles). In my opinion, the top MVP on the PHP forum is a tad more than just a "technical user"! :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top