PHPInclude Paths & Best Practices

Include Paths & Best Practices

Knowing include and require (covered on the Include & Require page) is only half the story. The other half is making sure the path you hand to them actually resolves to the right file — every time, regardless of which script triggered the request or what directory the server happened to be running from. Path bugs are one of the most common "works on my machine" problems in PHP, and almost all of them come down to confusing a relative path with an absolute one.

Relative paths are relative to the working directory, not the file

A beginner's instinct is to write include "../config.php"; and assume PHP resolves .. relative to the file that contains the include statement. It does not. A relative path passed to include/require is resolved relative to the current working directory of the PHP process — which is usually the directory of the script that was originally invoked, not the directory of whichever file is doing the including.

project/lib/loader.php

PHP
<?php
// Intent: load config.php from the project root, one level up from lib/
include "../config.php";

project/index.php

PHP
<?php
include "lib/loader.php";

When index.php is run directly, the working directory is project/, so loader.php's "../config.php" actually resolves to one directory above project/ — not to project/config.php as intended. This works by accident if you happen to run loader.php directly from inside lib/, and breaks the moment something else includes it. That inconsistency is exactly the kind of bug that only shows up after deployment, when the entry point or working directory changes.

The reliable fix: __DIR__

PHP provides the magic constant __DIR__, which always evaluates to the absolute path of the directory containing the file it appears in — no matter which script included that file or what the current working directory is. Building include paths with __DIR__ makes them immune to the problem above.

project/lib/loader.php (fixed)

PHP
<?php
require_once __DIR__ . "/../config.php";

Now __DIR__ inside loader.php always resolves to .../project/lib, so __DIR__ . "/../config.php" always resolves to .../project/config.php, regardless of whether the script was run directly, included from index.php, or included from somewhere three directories deep. The related constant __FILE__ gives you the absolute path of the current file itself (including the filename), which is useful for logging or for deriving __DIR__ manually on very old PHP versions, though in modern code __DIR__ is what you want for directory paths.

Comparing __FILE__ and __DIR__

PHP
<?php
echo __FILE__;
echo "\n";
echo __DIR__;
/var/www/project/lib/loader.php
/var/www/project/lib
__DIR__ is equivalent to dirname(__FILE__)
Before `__DIR__` was added in PHP 5.3, the idiom for the same result was `dirname(__FILE__)`. You may still see that pattern in older codebases and tutorials; it does exactly the same thing as `__DIR__`, just more verbosely.
Why relying on include_path is discouraged

PHP also supports a configuration setting called include_path (set in php.ini, or at runtime with set_include_path()), which defines a list of directories PHP searches through when a plain filename is given to include/require without it resolving directly. In theory, this lets you write include "config.php"; from anywhere and have PHP find it automatically, as long as its directory is listed in include_path.

Relying on include_path (avoid this)

PHP
<?php
// Assumes "/var/www/project/lib" is already in include_path
include "loader.php";

In practice this causes more problems than it solves. include_path is a piece of server configuration, not something checked into your project's source code, so the script's behavior now depends on a setting that lives outside version control. Move the project to a different server, a different hosting plan, or even a different developer's machine with a differently configured php.ini, and includes that relied on include_path can suddenly fail to resolve — with no clue in the codebase itself explaining why.

  • The path is invisible in the code — you cannot tell where a bare "loader.php" is expected to come from just by reading the file.

  • It couples your application to server configuration that isn't part of the project, making deployments and onboarding fragile.

  • It can silently pick up the wrong file if two directories in the search list both happen to contain a file with the same name.

  • Modern autoloaders (Composer’s PSR-4 autoloading) solve the "find this class/file automatically" problem far more predictably, without touching php.ini at all.

A changed working directory silently breaks bare relative paths
Cron jobs, CLI scripts invoked from a different directory, and code run inside some testing frameworks often have a different working directory than you'd expect from a normal web request. Any `include`/`require` written as a bare relative path (no `__DIR__`) that happens to work in the browser can fail the moment the same script runs from `cron` or `php artisan`-style tooling. Always anchor include paths to `__DIR__` rather than assuming a particular working directory.
Organizing a project so paths stay predictable

Beyond the mechanics of __DIR__, a well-organized directory structure makes include paths easier to reason about in the first place. A common layout for a small, framework-free PHP project looks like this:

A predictable project layout

Text
project/
├── config.php          # database credentials, environment settings
├── index.php           # single public entry point
├── includes/
│   ├── header.php
│   ├── footer.php
│   └── functions.php
├── classes/
│   ├── User.php
│   └── Order.php
└── public/
    ├── css/
    └── js/
  • One shared config.php at the project root, loaded with require_once __DIR__ . "/config.php" from anywhere that needs it.

  • A dedicated includes/ folder for templates and shared snippets, keeping page-specific files short.

  • A classes/ (or src/) folder for class definitions, ideally loaded through a Composer autoloader rather than individual require statements once the project grows.

  • Static assets kept out of the PHP folders entirely, under a public/ directory the web server points at directly.

With a layout like this, every include path can be expressed consistently as __DIR__ plus a fixed relative offset, because the directory structure itself is stable even as individual files come and go.

Tip
As a habit, never write a bare relative `include`/`require` path in new code. Build the path from `__DIR__` every time, even for files in the same directory (`__DIR__ . "/functions.php"`). It costs a few extra characters and guarantees the include keeps working no matter where the script gets called from next.