Reputation: 6274
I'm auditing my site design based on the excellent Essential PHP Security
by Chris Shiflett.
One of the recommendations I'd like to adopt is moving all possible files out of webroot, this includes includes.
Doing so on my shared host is simple enough, but I'm wondering how people handle this on their development testbeds?
Currently I've got an XAMPP installation configured so that localhost/mysite/
matches up with D:\mysite\
in which includes are stored at D:\mysite\includes\
In order to keep include paths accurate, I'm guess I need to replicate the server's path on my local disk? Something like D:\mysite\public_html\
Is there a better way?
Upvotes: 4
Views: 2033
Reputation: 6140
This seems to be a sticking point for quite a few php developers, so lets address it well. Most PHP applications litter their code with include '../../library/someclass.php.class'
. This isn't much good to anyone, because its very easy to break, and no-one likes doing path janitor work when you should be coding. It's also a bit like building a house of cards and cementing the joins for fear of any change. So ok, maybe we could just create a constant, and use the full path?
define('PATH', '/home/me/webroot/Application');
include(PATH . '/Library/someclass.php.class');
Well thats pretty good, but erm, what if we deploy on windows? Also, are we going to define path on every script entrance point? Not very DRY if you ask me. Plus, moving deployments is going to be a huge pain. Clearly, while we're closer it's not much of an improvement.
Luckily, PHP provides a few magic bullet functions that can help us out immediately.
So lets just say you have a single entrance point for your application, or at the very least a shared header file. We can grab our deployment root pretty quickly if we know where our header file is related the the code root. IE, in /home/me/webroot/Application/Init/set_paths.php
define('PATH_SITE', realpath(dirname(__FILE__) . '/../../'));
Awesome, thats our document root. It's OS independant and its pretty easy to adapt if you change where set_paths.php
lives. Now we can talk about some other locations in our application, just because constants are handy:
define('PATH_APPLICATION', realpath(PATH_SITE . "/Application"));
define('PATH_LIBRARY', realpath(PATH_SITE . "/Application/Library"));
define('PATH_CONFIG', realpath(PATH_SITE . "/Config"));
define('PATH_WRITE', realpath(PATH_SITE . "/Volatile"));
This is all very well and good, but its not really much better than our previous solution. Enter in the PHP include path. By adding the relevant constants to our path, we wont need to define them every time. Order of paths in the include path is actually pretty important for speed, so we make every effort to get them in order of usage.
$paths['inc'] = array_flip(explode(PATH_SEPARATOR, get_include_path()));
unset($paths['inc']['.']);
$paths['inc'] = array_flip($paths['inc']);
// The first item on the path the external
// libs that get used all the time,
// then the application path, then the
// site path, and any php configured items.
// The current directory should be last.
$paths = array_merge(array(PATH_LIBRARY, PATH_APPLICATION, PATH_SITE), $paths['inc'], array("."));
set_include_path(implode(PATH_SEPARATOR, $paths));
Now all the critical locations in our application are on the path, and you can include to your hearts content, regardless of where you decide to store your libraries, settings etc.
include('someclass.php.class');
A step further
If you're working with a fairly well designed OOP Application, we can go a bit further. If you subscribe to one file, one class, then the PEAR naming convention makes life very simple.
The PEAR naming conventions dictate a 1:1 relation between the filesystem and the class. As an example, the class Foo_Bar_Baz would be found in the file "Foo/Bar/Baz.php" on your include_path. source
Once you have a predictable mapping of files to classes, you can then implement spl_autoload_register And you can replace
include('someclass.php.class');
new SomeClass();
With simply
new SomeClass();
And have PHP deal with it for you.
Upvotes: 6
Reputation: 9397
Yes, there is a better way. You should always be using relative paths, as in include('./includes/foo.php');
. If your paths are relative, you don't have to worry about your local paths except that they should match the overall structure of the site (./includes
could refer to D:\projects\web\foo-page\includes
on your local machine and /home/andrew/foo-page/includes
on the site).
Alternately, use a web server on your local machine or a virtual machine to mimic your production environment; in a properly configured environment, /
will refer to your wwwroot
, not to your root directory (like filesystem /
or D:\
on Windows).
Upvotes: 4
Reputation: 2454
You could always have relative include paths. Either simply doing require("../../something"); instead of require("D:\something\something"); (Of course, in that case you have to make sure that number of .. before your path is correct. (.. means go to the parent directory)), or, if your include structure is very complex, you could use the FILE constant, which always points to the php file currently being executed. You could get that value, and then parse our the needed paths to your file.
Finally, if you want to keep the file structure as exact as in production server as possible, but don't want to keep a lot of files in different locations, look up junctions http://en.wikipedia.org/wiki/NTFS_junction_point for windows or symbolic links for *nix. That way you could build up the right paths using junctions, at the same time keeping your original files where they were, thus only keeping 1 version of files.
Upvotes: 1