|
Posted by Sebastian Lisken on 09/28/68 12:01
Hi,
I have noticed something in PHP's include mechanism that surprised me.
If you include a script that itself contains an include command using
a relative path, that path is interpreted from the position of the
'outermost' script, not that of the script in which the relative path
appears. Here is a simplified example, with error checking, php tags
etc. left out for clarity. I'll use the extension ".inc.php" to mark
scripts that are intended to be included.
Let x be the full path to some point in the file system, and imagine
that the server has been configured so that x/htdocs is reachable through
some URL, but x itself is not.
Say there is an include script doing database access and some more setup,
like this:
*** x/htdocs/setup.inc.php ***
require "../include/secrets.inc.php";
$link = mysql_connect($server, $user, $password);
mysql_select_db($database, $link);
/*
imagine more code that reads initial stuff from the database
*/
***
Because this script does more than just connect (and other included
scripts might also connect and do other "extra" things), the actual
secrets are taken out and appear just once, like this:
*** x/include/secrets.inc.php ***
global $server, $user, $password, $database;
$server = "...";
$user = "...";
$password = "...";
$database = "...";
***
x/include can not be reached by an URL, so we have implemented a common
piece of security advice.
Now of course we have scripts in, say, x/htdocs/script1.php, that use
this setup through the statement
*** x/htdocs/script1.php (extract) ***
include "setup.inc.php";
***
This all looks fine, but it fails if x/htdocs/setup.inc.php is included
from files at other depths in the file tree under x/htdocs. If I have
a script x/htdocs/subdir/script2.php that says:
*** x/htdocs/subdir/script2.php (extract) ***
include "../setup.inc.php";
***
then the relative path "../include/secrets.inc.php" in setup.inc.php
causes PHP to look for "x/htdocs/include/secrets.inc.php", as reported
by an error caused by the "require" statement. It must therefore be the
case that the relative path after "require" is interpreted relative to
x/htdocs/subdir/script2.php - my expectation would have been that it's
relative to x/htdocs/setup.inc.php, i.e. to the script in which the
"require" statement actually appears.
I do have a solution that I'm not fully comfortable with: after the
"require" within x/htdocs/setup.inc.php I put an expression using regular
expression replacement and __FILE__, similar to this:
*** x/htdocs/setup.inc.php (extract) ***
require ereg_replace('/htdocs/.*', '/include/secrets.php', __FILE__);
***
ereg is used instead of preg here because preg would include the first
slash as a delimiter of the regular expression, not a required character.
You will know that __FILE__ behaves somewhat similarly to the
"include" mechanism: it evaluates to the path of the 'outermost' script,
i.e. "x/htdocs/script1.php" or "x/htdocs/subdir/script2.php". One reason
for my discomfort is that I can't exactly know where within __FILE__
the string "/htdocs/" appears. It will appear at least once because my
scripts are under x/htdocs - but what I call "x" will be a longer path
that might in theory contain "/htdocs/" somewhere earlier.
I'd be happier with an expression that, if used in x/htdocs/setup.inc.php,
would evaluate exactly to "x/htdocs/setup.inc.php" - some special variable
whose meaning would be 'the script file this line actually appears in'. I
haven't found such a variable. Have I missed something?
I could use $_SERVER["DOCUMENT_ROOT"] to construct an absolute path
within the server's file system, but for that I'd need to know where
x is in relation to the document root. (It might not even be under that
root if aliases are used.) So I'd prefer to get by without that knowledge.
Of course I could get rid of my excessively complicated setup and
avoid the 'double include' structure. I'm considering that anyway, but
I'd expect others to stumble over the same expectation of how 'double
includes' work, so I'd be curious about other solutions.
So I'm wondering: have other more experienced PHP programmers come across
the same problem, and is there a 'canonical' solution?
Thanks for reading all this, and for your replies.
Sebastian Lisken
Navigation:
[Reply to this message]
|