Determining the Base Path in PHP
Determining the Document Root, also known as the Base Path, is important when you want to include files or work with the file system; this solution should be very flexible and broadly compatible.
By. Jacob
Edited: 2021-02-28 16:14
When including files or working with the file system from PHP scripts, it is best to use absolute paths; however, knowing the base path of a project can actually be quite difficult, since there might be inconsistencies between systems. Instead, I now define my own BASE_PATH at the composition root of my projects (Usually the index.php file).
The problem is that the document root might not refer to what we expect, and it has other inconsistencies as well, such as missing a tailing slash on directories. To avoid this, I now define my own base path variable, using forward slash as directory separator — and for directories, a tailing slash is always be included.
All paths should be written using forward slash as directory separator, since this is supported by most systems.
Windows will also work just fine using forward slash — in fact, even using a mix of backslash and forward slash should work fine — for consistency, it is however best if we use the same throughout the code.
Another reason for this policy is that backslash might cause problems in some circumstances, since it has special meaning in PHP, and therefore needs to be escaped when used in paths, even when used within single quotes..
From PHP 5.3 and onwards, we can use __DIR__:
define('BASE_PATH', rtrim(preg_replace('#[/\\\\]{1,}#', '/', __DIR__), '/') . '/');
In older versions:
define('BASE_PATH', rtrim(preg_replace('#[/\\\\]{1,}#', '/', realpath(dirname(__FILE__))), '/') . '/');
Handling includes
At the top of an index file we usually define a BASE_PATH constant. This constant can be used when including files via include or require, and can also be accessed from within the included files, though this is best avoided.
If some class methods depends on BASE_PATH, it is probably better to pass it as an argument to the method, or via dependency injection through a settings object.
Below is an example of how BASE_PATH is defined:
define('BASE_PATH', rtrim(preg_replace('#[/\\\\]{1,}#', '/', realpath(dirname(__FILE__))), '/') . '/');
The code works like this:
- realpath(dirname(__FILE__)) creates the absolute path for the parent directory of the running script.
- preg_replace makes sure we only use forward slash as separator. Note. Backlash needs to be escaped like this "\" when used in regular expressions.
- rtrim() removes a trailing slash (if any).
- Finally a trailing slash is added manually. This is needed to avoid confusion when using the BASE_PATH constant.
Dynamically including files
Currently developers need to remember using forward slash consistently. This is not a huge problem since most will probably be developing on Linux anyway. But, a solution could be to include files via a function or asset loader instead.
This is not something we are currently doing, but it would also be a useful way to dynamically include files if this is something we might need in the future.
$files_to_include = array(
BASE_PATH . 'lib/db_class/db_class.php',
BASE_PATH . 'lib/translator/translator_class.php'
);
function dynamic_require($files_array) {
foreach ($files_array as $file) {
require preg_replace('#[/\]{1}#', '/', $file);
}
}
DIRECTORY_SEPARATOR is not recommended
It is not recommended that developers use DIRECTORY_SEPARATOR, since most systems will understand forward slash just fine. There might still be niche cases where it can be useful, but we avoid it for includes.
DIRECTORY_SEPARATOR is a predefined constant in PHP. It contains the default directory separator used on the system where the script is running.
Tell us what you think: