Michael_B Michael_B - 28 days ago 23
PHP Question

Setting the path for include / require files

All my PHP include files are in a single directory:

https://www.mywebsite.com/includes


Inserting these files in top level pages is easy enough:

<?php include 'includes/logo.php'; ?>
<?php include 'includes/main_nav.php'; ?>
<?php include 'includes/news.php'; ?>
etc.


For sub-directory pages I've been doing this:

<?php include '../includes/logo.php'; ?>
<?php include '../includes/main_nav.php'; ?>
<?php include '../includes/news.php'; ?>


and this:

<?php include '../../includes/logo.php'; ?>
<?php include '../../includes/main_nav.php'; ?>
<?php include '../../includes/news.php'; ?>


So far so good, but I suspected it wasn't going to continuing being this easy.

Now I need to include this file:

top_five_news_stories.php


in this:

news.php


At this point, my relative path strategy fails because the include in the include can have only one path structure.

I've read several posts recommending absolute paths using:


  • dirname(__FILE__)

  • realpath(dirname(__FILE__)

  • $_SERVER["DOCUMENT_ROOT"]



However, they all come with some kind of caveat relating to PHP configuration, server configuration or operating system. In other words, there's often a comment by somebody saying it didn't work in their case, or doesn't work in IIS, or doesn't work in UNIX, or something else.

A solution I haven't seen is one I thought would be most simple: Just set a variable:

$base = "https://www.mywebsite.com/includes";


then:

<?php include $base . "logo.php" ?>


Considering that I already use the HTML
base
element
, which works in a similar way, this method strikes me as simple and efficient.

But since it wasn't mentioned in any of the posts I read, I'm wondering if I'm overlooking a potential problem.

Frankly, if I had to go to production to today, I would use this:

<?php include $_SERVER['DOCUMENT_ROOT'] . '/logo.php" ?>


which works for me and is commonly mentioned.

But I'm wondering if using a variable is a reliable, efficient method?

Answer

Don't

I would advise against using anything that needs something outside of PHP, like the $_SERVER variable.

$_SERVER['DOCUMENT_ROOT'] is usually set by the webserver, which makes it unusable for scripts running from the command line. So don't use this.

Also don't use url's. The path-part in a url is not the same as the path of the file on disk. In fact, that path can not even exist on disk (think Apache rewrites).

Including url's also needs you to turn allow_url_include on, which introduces (severe) security risks if used improperly.

Do

If your minimal supported PHP version is 5.3 (I hope it is!), you can use the magic constant __DIR__. 2 examples:

define(ROOT_DIR, __DIR__);
define(ROOT_DIR, realpath(__DIR__ . '/..'));

If you need to support lower versions, use dirname(__FILE__). 2 examples:

define(ROOT_DIR, dirname(__FILE__));
define(ROOT_DIR, realpath(dirname(__FILE__) . '/..'));

Make sure ROOT_DIR points to the root of you project, not some subdirectory inside it.

You can then safely use ROOT_DIR to include other files:

include ROOT_DIR . '/some/other/file.php';

Note that I'm defining a constant (ROOT_DIR), not a variable. Variables can change, but the root directory of you project doesn't, so a constant fits better.

realpath()

realpath() will resolve any relative parts and symlinks to the canonicalized absolute pathname.

So given the following files and symlink:

/path/to/some/file.php
/path/to/another/file.php
/path/to/symlink => /path/to/another

and /path/to/file.php contains:

define(ROOT_DIR, realpath(__DIR__ . '/../symlink'));

then ROOT_DIR would become /path/to/another, because:

  • __DIR__ equals to /path/to/some (so we get /path/to/some/../symlink)
  • .. is 1 directory up (so we get /path/to/symlink)
  • symlink points to /path/to/another

You don't really need to use realpath(), but it does tidy up the path if you're relying on relative parts or symlinks. It's also easier to debug.

Autoloading

If you need to include files that contain classes, you'd best use autoloading. That way you won't need include statements at all.

Use a framework

One last pease of advise: This problem has been solved many many times over. I suggest you go look into a framework like Symfony, Zend Framework, Laravel, etc. If you don't want a "full stack" solution, look into micro-frameworks like Silex, Slim, Lumen, etc.

Comments