Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 835
Michael Benjamin Avatar asked Oct 21 '16 21:10

Michael Benjamin


People also ask

What are include () and require () functions?

The include (or require ) statement takes all the text/code/markup that exists in the specified file and copies it into the file that uses the include statement. Including files is very useful when you want to include the same PHP, HTML, or text on multiple pages of a website.

What is the PHP include path?

Example 1: Include path is a relative path to a PHP page at web root.

What is __ DIR __ in PHP?

The __DIR__ can be used to obtain the current code working directory. It has been introduced in PHP beginning from version 5.3. It is similar to using dirname(__FILE__). Usually, it is used to include other files that is present in an included file.

What is require and include in PHP?

The 'include' or 'require' statement can be used to insert the content of one PHP file into another PHP file (before the server executes it). Except in the case of failure, the 'include' and 'require statements' are identical: Include in PHP will only generate an alert (E_WARNING), and the script will proceed.


2 Answers

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.

like image 193
Jasper N. Brouwer Avatar answered Oct 09 '22 11:10

Jasper N. Brouwer


Jasper makes some good points, and a further reason for not using DOCUMENT_ROOT is that content accessible via a URL does not have to be within this directory (consider Apache's alias, scriptalias and mod_user_dir, for example).

As Barmar points out PHP explicitly provides functionality for declaring a base directory for includes. While this is typically set in the config it can be overridden/added to at runtime in your code. You never want to see a variable in your include/require directives. It breaks automatic tools and hides vulnerabilities. Nor should you ever include using the file wrappers.

There is an argument in OO programming for never using include/require explicitly but just autoloading class definitions. However the problem of locating the code remains.

The short answer is that there is no best solution for the problem you describe. Each method has its drawbacks - the best solution is completely dependent on the context. For an enterprise application, setting the include_path simplifies development processes and, if not directly accessible from the webserver enhances security. It also allows for selectively overlaying functionality by manipulating the order of multiple entries in the path.

On the other hand this is not a good model for software you intend to distribute to less technical users likely to be confused about multiple paths whom may not have access to directories outside the document root or to change the default config.

Using relative paths is a robust and portable solution. I don't understand your problem with including top_five_news_stories.php.

A solution which gives you the benefits of both the enterprise and low-end hosting is shown below. This however has the drawback that it needs code added to each entry point in the site (and requires the app to be installed in a named sub directory):

define('APP_NAME', 'mikesdemo');
$base=substr(__DIR__, 0, strrpos(__DIR__, APP_NAME))
      . APP_NAME . '/include';
set_include_path(get_include_path() . PATH_SEPARATOR . $base);

The more sophisticated user can then simply....

mv /var/www/html/mikesdemo/include/* /usr/local/php/include/
like image 2
symcbean Avatar answered Oct 09 '22 11:10

symcbean