5

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?

Michael Benjamin
  • 265,915
  • 79
  • 461
  • 583
  • 1
    Don't use URLs in `include`. When you access a PHP script through a webserver, it executes it at the remote end, it doesn't return the source code, but `include` needs source code. – Barmar Oct 21 '16 at 22:21
  • Take a look at the `include_path` setting in `php.ini`. Put your include files in that directory, then you can access them from any scripts. – Barmar Oct 21 '16 at 22:24
  • http://php.net/manual/en/ini.core.php#ini.include-path – Barmar Oct 21 '16 at 22:24

4 Answers4

12

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.

Jasper N. Brouwer
  • 20,417
  • 4
  • 49
  • 76
  • Great answer. It helped me a lot to better understand what needs to be done. Thank you. – Michael Benjamin Nov 06 '16 at 18:34
  • With regard to your advice about frameworks, I'm re-designing an existing enterprise-level site from scratch. I'm using frameworks *that I'm aware of* where appropriate. The frameworks you mention at the end of your answer are new to me. I will look into them. – Michael Benjamin Nov 06 '16 at 18:38
2

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/
symcbean
  • 45,607
  • 5
  • 49
  • 83
1

File Arquitecture Rework:

define a path for every tipe of file like that:

+root
|
+------app(D)(all php script MVC)
|
+------conf(D)(all php Config file)
|
+------assets(D)(all file js and css static image)
|
+------fileup(D)(all file Uploades)
|
+------index.php(F)(Procesor of petition http)

in your index you need include all File of config Like style C++:

Example:

require_once ('conf/config.security.php'); #Configuration around Security in PHP
require_once ('conf/config.conpro.php'); #Configuration around Constantent
require_once ('conf/config.classlib.php'); #Lib class around Generic DB Conection ETC
require_once ('conf/config.classlibmvc.php'); #Lib class around MVC specific class

And example of config file:

Declare Root Directory + Path that shared the file

$APP_DIR_CLASS          =       $_SERVER['DOCUMENT_ROOT']   .   '/app/classgeneric/';

Define library file:

if (!defined('DBMANAGER_CLASS'))                define('DBMANAGER_CLASS'            ,'class.managerdb.php'          );

Include or require the class

require_once $APP_DIR_CLASS     . DBMANAGER_CLASS;

when you are in a class and need use DB class you can call it easy:

class Class_Exmaple{
    public function __construct(){
        $this   ->  DBMANAGER               =   new Class_BDManager();
    }
    public function __destruct(){
        $this   ->  DBMANAGER               =   new Class_BDManager();
    }
    public function ConsultDB(){
        $query ='Selec * From Tablename';
        $result = $this -> DBMANAGER -> ExecuteQ($query);
        print_r(result);
    }
}

is a easy way to implement but you need learn more about injection and Class Loaders.

  • Thank you for your answer. It's useful in terms of general knowledge, but I'm having trouble applying it to my specific situation. If you have any additional recommendations please advise. Here's a quick summary of my question above: https://jsfiddle.net/2npxxLwa/1/ – Michael Benjamin Nov 06 '16 at 19:04
  • can you share the content of a include file? –  Nov 06 '16 at 20:32
  • It's just text. Building the framework. No actual content yet – Michael Benjamin Nov 06 '16 at 20:34
1

There is no "correct way" to require/include an internal script in your project. A lot (most) MVC frameworks use similar best practices to global file access in a router object.

Let's take an example, here is our directory infrastructure:

App/
    Controllers/
        Controller.php
    Models/
        Model.php
    Views/
        View.php
    404/
        index.php
index.php
.htaccess

Inside our .htaccess we would have a rewrite rule to the index.php in the root directory of your server.

Inside this file, is where we actually run the whole of your Software. For example, this is a great router I use AltoRouter.

First things first, we need to add a way to stop direct browser access and an error path to any controllers, models and views:

define( 'ERROR_PATH', strtolower(explode( '/', $_SERVER['SERVER_PROTOCOL'][0]) . '://' . $_SERVER['SERVER_NAME'] . '/404' );
define( 'IN_APP', 0 );

Which is then used in your controllers, models and views like:

if( !defined( 'IN_APP' ) )    {
    header('Location: ' . ERROR_PATH);
    exit();
}

Your file path will be the root file path if you declare __FILE__ in this instance (index.php) so we can use it any way (best practice is global defines).

 define( '_DIR_', dirname(  __FILE__ ) );

Then start requiring your files:

$includeFiles = [
    glob( _DIR_ . '/Controllers/*.php' ),
    glob( _DIR_ . '/Models/*.php' )
];

foreach( $includeFiles as $dir ):
    foreach( $dir as $file ):
        require_once( $file );
    endforeach;
endforeach;
Jaquarh
  • 5,698
  • 4
  • 19
  • 49
  • Thanks for your answer. And I appreciate your mentioning how to prevent direct browser access to the include/require files, as that is an issue I've been thinking about, but hadn't yet tackled. I'm still going through the answers here to develop the right solution. Here's a summary of the question if you want to add more: https://jsfiddle.net/2npxxLwa/ – Michael Benjamin Nov 06 '16 at 18:53
  • Looks like the simplest way to prevent direct browser access to a directory with include / require files is to put an `.htaccess` file in said directory with `"deny from all"`. Working in all my tests so far. http://stackoverflow.com/a/409511/3597276 – Michael Benjamin Nov 07 '16 at 01:25
  • Yeah, but using `.htaccess` is not *reliable* because this stops access to **images** too. It's best to dedicate all requests that **do not** ***exist*** to a index file and then route requests there. Then only allow specific files to be accessed with the `define()` and `defined()` methods. Basic best practice used in many open-source web applications like `MyBB` @Michael_B – Jaquarh Nov 07 '16 at 23:21