Path of current PHP script relative to document root

TL;DR: What alternatives to the last code sample on this page are there when trying to use relative links on pages included with PHP's include command?


I'm trying to include() a file that links to a .css document, the problem is that I need to include this file from multiple (different) directories. My directory structure looks somewhat like this:

www
├── (getpath.php)
│
├── css
│   └── style.css
├── php
│   └── header.php
└── content
    ├── index.php
    └── posts
        └── index.php

(I'm including header.php in both index.php files. header.php is the file that refers to style.css (which the browser needs to be able to find). Example: When using ../css/style.css, the CSS will show up in my content/index.php but not in my content/posts/index.php, because the browser is looking for content/css/index.php.)

The website should also work when placed in a subdirectory of the document root, not in the document root itself. For this reason, I can't just use /css/style.css (relative to the server root) to link to my CSS stylesheet.

I think (please correct me if I'm wrong!) that the easiest way to tell the browser where to find the CSS document is probably to specify the path relative to the document root. As I expect my website to be in a subdirectory of the document root (and want to avoid using a config file to store the subdirectory in), I need to find out the relative path of the subdirectory the website is in. My idea was to place a file called getpath.php in my website's root directory (not the document root). In that file, I wrote the following:

<?php
  define('PROJECT_SUBDIRECTORY', $_SERVER['PHP_SELF']);
?>

However, I noticed that this gives me the relative directory of the PHP file that the user requested in the browser (e.g. content/index.php) instead of the included file (getpath.php). I've also read that PHP_SELF is based on REQUEST_URI, which wouldn't be secure. I've found this solution here:

define('PROJECT_DIR', preg_replace('/^' . preg_quote($_SERVER['DOCUMENT_ROOT'], '/') . '/', '', __DIR__));

It works perfectly fine, but I'm wondering if there's a "better" way to do this. I didn't manage to find a variable for that — is the solution above really the only way to do that?

Should I use the previous code example or is there a better way? Should I change my directory structure? How do other people solve this problem?


Edit: I have multiple (completely independent) websites on my webserver, they're all in subdirectories of the document root (like /documentroot/project1/, /documentroot/project2/, ...) I'm trying to get the websites to work in subdirectories. I could obviously send CSS to the browser via /css/style.css, but that doesn't work if the website is in a subdirectory. I also want to avoid using a config file to specify the path of my project relative to the document root.


Another edit: The problem is that every php file in my project /documentroot/project1 includes a navigation bar by using PHP's include function. The navigation bar is saved as /documentroot/project1/php/header.php and the css for the navbar as /documentroot/project1/css/style.css. When using relative links (to tell the browser where style.css is), the browser still thinks the path is relative to index.php rather than header.php, so it searches for the CSS in the same directory as index.php. Problem: There are multiple index.php files in different directories, and I don't want to copy & paste my CSS folder in every single directory that contains a index.php file.

Answers


What I've done in the past is to use a setup like this:

define('DIR_BASE',     dirname( __FILE__ ).'/'); 
define('DIR_SYSTEM',   DIR_BASE.'app/');
etc.

This would be placed in a file root of your project, which will give you access to other areas of your file structure relative to the root.

For your case, it would look like this:

define('DIR_BASE',     dirname( __FILE__ ).'/');
define('DIR_CSS',      DIR_BASE.'css/');
define('DIR_PHP',      DIR_BASE.'php/');
define('DIR_CONTENT'   DIR_BASE.'content/');

This would go in getpath.php in your root, and would give you the ability to reference any file or directory regardless of where it is placed in the directory structure (be sure to include it wherever you'll be using them). You wouldn't need to change your directory structure or anything like that, and you don't have to worry about any vulnerabilities with something like this, since it's internal.

Edit I keep coming back to the structure of the system. Is this a multi-site system that uses the same CSS through out? If not, then what is the justification for having the directory structure laid out like this? Can you give just a little more detail please?


Looks like the best way to do this is removing $_SERVER['DOCUMENT_ROOT'] from __DIR__, as I mentioned in the original question.

define('PROJECT_DIR', preg_replace('/^' . preg_quote($_SERVER['DOCUMENT_ROOT'], '/') . '/', '', __DIR__));

I think I'm just going to do it like that.


I have a config/ folder in my project root with a file called main.php, I use this:

$root = $_SERVER['DOCUMENT_ROOT'];
$dir = dirname(__DIR__);
$root = str_replace("\\","/",$root);
$dir = str_replace("\\","/",$dir);
$web_dir = str_replace($root,'',$dir);
if ($web_dir!=='') { $web_dir = '/'.$web_dir; }
define('DIR',$web_dir);

this removes the doc root from the full directory path of my config file, thus giving me the actual path relative to web root. If your config file was in the root of your project you would remove this line:

$dir = dirname(__DIR__);

relative URL, or where your script located relative to document root

preg_replace('/[\\\\\\/]+/', '/', '/' . substr(__DIR__, strlen($_SERVER['DOCUMENT_ROOT'])) . '/' )

Need Your Help

Cross-browser HTML5 Drag and Drop JSON DataTransfer fails

javascript html5 cross-browser xss html5-draggable

I came to notice that for some reason it is not possible to transfer data from chrome to firefox or viceversa through a draggable object when the mime is set to application/json.