Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Organizing PHP includes in your development environment

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?

like image 804
Drew Avatar asked Dec 21 '22 19:12

Drew


1 Answers

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.

  • set_include_path
  • get_include_path
  • realpath

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.

like image 57
jhogendorn Avatar answered Dec 24 '22 07:12

jhogendorn