How to programmatically determine the document root in PHP? - php

Here's a problem that I've been running into lately - a misconfigured apache on a webhost. This means that all scripts that rely on $_SERVER['DOCUMENT_ROOT'] break. The easiest workaround that I've found is just set the variable in some global include files that is shared, but it's a pain not to forget it. My question is, how do I determine the correct document root programatically?
For example, on one host, the setup is like this:
$_SERVER['DOCUMENT_ROOT'] == '/htdocs'
The real document roots are:
test.example.com -> /data/htdocs/example.com/test
www.example.com -> /data/htdocs/example.com/www
And I'd like a script that's run from www.example.com/blog/ (on the path /data/htdocs/example.com/www/blog) to get the correct value of /data/htdocs/example.com/www.
On another host, the setup is a bit different:
$_SERVER['DOCUMENT_ROOT'] == '/srv'
test.example.com -> /home/virtual_web/example.com/public_html/test
www.example.com -> /home/virtual_web/example.com/public_html/www
Is there any solution to this? Or is the only way simply not to ever rely on $_SERVER['DOCUMENT_ROOT'] and fix all the software that I'm running on my sites? Fixing this on the hosting's side doesn't seem to be an option, I've yet to encounter a host where this is was configured correctly. The best I got was a document root pointing to www.example.com, which was at least inside open_basedir - they used yet another naming scheme, www.example.com would point to /u2/www/example_com/data/www/.

In PHP5 there is the magic constant __FILE__ that contains the absolute path of the file in which it appears. You can use it in combination with dirname to calculate the document root.
You can put a statement like the following one in a config file
define ('DOCUMENT_ROOT', dirname(__FILE__));
this should do the trick

There's no need to modify all scripts.
You can run PHP file before any script is run using auto_prepend_file.
$_SERVER is just an array, you can modify it and set correct $_SERVER['DOCUMENT_ROOT'].

This is one reason why people siphon everything through a bootstrap /index.php using htaccess and/or query strings. You can use the dirname( __FILE__ ) trick noted above and get the public base of your app that way.
If you're too far into it to switch to a single entry point, one thing I've seen people do is have a common header to their script which walks up the directory tree to find a file which is unique to the base dir:
function findAppBase( $dir ) {
if( file_exists( "$dir/unique_file.txt" ) ) {
return $dir;
return findAppBase( dirname( $dir ) );
}
$base = findAppBase( dirname( __FILE__ ) );
That code hasn't been tested, and there might be a slicker way using the vars in $_ENV or $_SERVER that will do what you want...

Based on http://www.helicron.net/php/:
$localpath=getenv("SCRIPT_NAME");
$absolutepath=getenv("SCRIPT_FILENAME");
$_SERVER['DOCUMENT_ROOT']=substr($absolutepath,0,strpos($absolutepath,$localpath));
I had to change the basename/realpath trick because it returned an empty string on my host. Instead, I use SCRIPT_FILENAME. This probably won't work on IIS anymore (but the original scripts that used the $_SERVER variable probably wouldn't either).

PHP should be setting the current directory to the one the script is in, so as long as that's not broken you should be able to figure out the document root using $_SERVER['SCRIPT_FILENAME'] and getcwd(). (I can't remember all the $_SERVER vars off the top of my head, there might be something in phpinfo() that's more useful.)

Why not demand that your webhost configures his servers correctly?
these kind of things tend to linger silently in your code and never get removed (but still active) until someone finally fixes the server. Thén everything will break again.
Or, move your stuff to a host that will work. If this is broken, who knows what you will find next.

Related

PHP include path different on different servers

In my site, i have a place that says:
include('../tpls/header_home.php');
and it works. Now i am moving the site to another server, where i get the error:
Warning: include(../tpls/header_home.php) [function.include]: failed to open stream: No such file or directory in /home/usern/public_html/site/tpls/static/home.php on line 4
now if I replace the line with this one, it works:
include('site/tpls/header_home.php');
I could change it, but i don't know how many more places I have of broken paths all around, not only in templates, but files, uploads etc.
I would rather fix the problem generally from a setting or something else I am missing. What do I do?
HINT:
Old working server gives me CWD:
/home/ortho/public_html/site/lib
New server that is problematic gives me CWD:
/home/orthosho/public_html
why?
STRUCTURE:
/home/usern/public_html/site/tpls/static/home.php
/home/usern/public_html/site/tpls/header_home.php
PHP old server: 5.2.17
PHP new server: 5.3.8
maybe that is causing problems?
Well, you basically answered your own question.
Create a settings file where you keep all of these specific paths as variables or otherwise, and include that file wherever you need to reference the paths. That's generally how it works.
settings.inc.php:
$_CONFIG['BASE_DIR'] = "/var/www/yoursite.com/";
some_file.php:
require 'settings.inc.php';
include $_CONFIG['BASE_DIR'] . "some_dependency.php";
I assume you are trying to fix the issue having already made the entire system, without a common settings file, in which case I don't have an answer. You should really centralise things as soon as you start coding to avoid these kinds of issues.
You must have config file, which will have the abosolute path to your site, ofc it will be relative (no need to be hardcoded url) and then use it in all include calls.
Let me explain you, lets say you have a folder in your site called config and in in file called config.php. If you want you could make config class that will hold all your important variables. One of them should be
$ROOT_PATH = str_replace( '\\', '/', dirname( dirname( __FILE__ ) ) ) . '/';
That will be root path relative to your project. Then you can simply call this variable elsewhere when you include something:
require_once $ROOT_PATH . 'blabla/test.php';
Alternative to fix your broken code is to redefine include path with set_include_path()
http://php.net/manual/en/function.set-include-path.php
But for future, learn to code smart :)
Given your new information, the problem is the following:
Old server => ~/public_html/site/lib
New server => ~/public_html
Why? Well, it's because you've set it that way. How do you access the previous website? yourdomain.com/site/, or just yourdomain.com? If it's the first one, then it's bound to be that you've got different web roots.
Now, what you're trying to include is this:
Old server => ~/public_html/site/tpls/...
New server => ~/tpls/
That doesn't quite look right does it?
My guess is that you'd need to alter the document root in httpd.conf in some way.
Sometimes it helps to use dirname so its really relative to the current file. this is what I use if I make cronjobs, and its more "portable" this way
<?php
include dirname(__FILE__)."/../tpls/header_home.php";

DOCUMENT_ROOT is not complete, missing domain folder

I have searched many threads so far but cant seem to find a solution. Inside one of my php scripts I am trying to get a server document root but the value I get is not complete, its simply missing the domain folder. I believe it is due to sharing hosting or smth else.
Here is the current way I am using:
$root = realpath($_SERVER["DOCUMENT_ROOT"]);
and the path I get is like:
/home/content/01/0151247/html
although I know it should be like:
/home/content/01/0151247/html/mydomain
I know as I compared it with SCRIPT_NAME and I see the mydomain there in the path.
Hope someone could direct me.
Thank you and sorry for probably asking another thousand time same question over community, I really tried things around from here, nothing helps me so far.
UPDATE
unfortunately I cant not simply use my index file with DIR as it is a wordpress setup and I am working on a separate folder where I am including some wordpress functionality but for that I need a document_root. If that would help.
UPDATE
apparantly the following way resolved my case, maybe it will help someone one day:
realpath($_SERVER["SUBDOMAIN_DOCUMENT_ROOT"]);
basically because of the server setup and domain configured as a subdomain.
Thanks to all who participated.
Prior to PHP 5.3 you can put a file in the directory whose path you want and define a constant:
define('ROOT_DIR', dirname( __FILE__ ));
After 5.3 you can just do:
define('ROOT_DIR', __DIR__);
The idea being that this would be in config.php of some sort that is included every time the application runs.
Magic Constants Docs
UPDATE
In the config file, you can just append the DOCUMENT_ROOT variable:
$_SERVER['DOCUMENT_ROOT'] = $_SERVER['DOCUMENT_ROOT'] . '/mydomain';
And that should take care of it for you.
Old Solution
The DOCUMENT_ROOT is an environment variable set by the server. So if this is on shared hosting, you cannot changed. An alternative is to set your own constant to it, so in a config type file that is included on your pages you can do something like:
define('DOCUMENT_ROOT', $_SERVER['DOCUMENT_ROOT'] . '/mydomain');
And then just use that constant in place of $_SERVER['DOCUMENT_ROOT']. The other option is to contact your host and inquire about it, maybe it was an oversight on their part and they will fix it.
EDIT
Probably using the __DIR__ as others have posted about is the better way, as the DOCUMENT_ROOT can be set to different items and at least with the __DIR__ you should get an accurate directory each time.
Personally, to get the root of a folder in PHP, I use this in the my index file:
define('ROOT', dirname(__FILE__)); // __DIR__ will work under PHP 5.3

How do you developers set your paths for templates?

I thought I would ask in case I could do it a better way.
On my local (WAMP) I have all my website in the www folder. ( C:\wamp\www )
Now currently I do this when i include a file:
require_once("".$_SERVER['DOCUMENT_ROOT']."/lib/config.php");
When I am working on local and upload site to a webhost i want to ensure the paths don't breakI
Could someone please tell me if I should be doing it this way?
I want to ensure maximum compatibility; meaning that paths won't break if I, for example, move site from local to whatever web host I decided to use or if I, for example, move from one host to another.
Maybe there is a more bullet proof way of doing it?
The problem with using $_SERVER['DOCUMENT_ROOT'] is that it will break if you move your PHP scripts up or down a directory level. Instead use this:
require_once(dirname(__FILE__) . "/lib/config.php");
__FILE__ is the absolute path of the script. dirname() removes the last path component (the script filename) so you can append other path components to it, like /lib/config.php or /../../lib/config.php or whatever. This will work everywhere.
PHP 5.3 introduced a shorthand for dirname(__FILE__), called __DIR__, but this doesn't work in <5.3.
You should see Include path. For that set_include_path is useful.
What I usually do, is make 1 config file (which might include others) with a few very basic constants:
define('PROJECT_ROOT', dirname(dirname(__FILE__))); // or dirname(__DIR__) for PHP 5.3
define('PROJECT_WEB', $_SERVER['DOCUMENT_ROOT']);
// etc
Al my other files/includes will be based on those very simple constants. I will never need relative paths and never the include_path, because both PROJECT_ROOT and PROJECT_WEB are 'real'/absolute.
Other useful (?) constants would be PROJECT_LOGIC and/or PROJECT_CONTROLLERS and/or PROJECT_3RD_PARTY etc.
That works fine for including the config file (although i would get rid of the beginning quotes)
require_once( $_SERVER['DOCUMENT_ROOT'] . "/lib/config.php" );
This is really the only way to do it if you are including the config file from a bunch of different directories.
On bigger project id say that "most" developers have a front controller that all scripts are loaded from. This front controller loads the config file and since its the same file always including the config file there's no need for $_SERVER['DOCUMENT_ROOT'].

How do you get PHP, Symlinks and __FILE__ to work together nicely?

On localhost. I have the following directory structure:
/share/www/trunk/wp-content/plugins/otherfolders
/share/www/portfolio/wp-content/symlink
Where symlink is a symbolic link to /trunk/.../plugins/. Basically, this is because I need to test multiple WordPress installs and set them up, but I don't want to have to move plugins around and copy and paste them everywhere.
However, sometimes I need to crawl up the directory tree to include a config file:
$root = dirname(dirname(dirname(dirname(__FILE__))));
if (file_exists($root.'/wp-load.php')) {
// WP 2.6
require_once($root.'/wp-load.php');
}
The folder always resolves to:
/share/www/trunk
Even when the plugin is being executed and included in
/share/www/portfolio/.
Is it possible in PHP to include files in the share/www/portfolio directory from a script executing in a symlink to the /share/www/trunk/.../plugins directory?
While this problem only happens on my test server, I'd like to have a safely distributable solution so crawling up an extra level is not an option.
The problem that I see with your code is that __FILE__ resolves symlinks automatically.
From the PHP Manual on Magic Constants
... Since PHP 4.0.2, __FILE__ always contains an absolute path with symlinks resolved ...
You can try using $_SERVER["SCRIPT_FILENAME"] instead.
$root = realpath(dirname(dirname(dirname(dirname($_SERVER["SCRIPT_FILENAME"])))));
if (file_exists($root.'/wp-load.php')) {
// WP 2.6
require_once($root.'/wp-load.php');
}
Note that I added the realpath() function to the root directory. Depending on your setup, you may or may not need it.
EDIT: Use $_SERVER["SCRIPT_FILENAME"] instead of $_SERVER["PHP_SELF"] for the file system path.
Here is the solution to that issue: https://github.com/logical-and/symlink-detective
$root = dirname(dirname(dirname(dirname(__FILE__))));
if (file_exists(SymlinkDetective::detectPath($root.'/wp-load.php'))) {
// WP 2.6
require_once(SymlinkDetective::detectPath($root.'/wp-load.php'));
}
or you can try that
try {
$root = dirname(dirname(dirname(dirname(__FILE__))));
require_once SymlinkDetective::detectPath($root.'/wp-load.php', '',
false /* this would throw an exception if file doesn't exists */);
}
catch (Exception $e) {
// nothing to do if file doesn't exists
}
You can use this code snippet to get a path where symlinks are not resolved.
If you don't have bash available, there's probably a different command you can use, but it works on linux enviroments.
I do think it's a malpractice that php resolves symlinks in FILE, since there's no way to get the path with symlinks. Otherwise we could easily get it using realpath.
Oh well.
<?php
$output = array();
exec('pwd', &$output);
define('__LINK__', $output[0].substr(__FILE__, strpos(__FILE__, DIRECTORY_SEPARATOR)));
?>
In some case its possible to change working dir and use getenv('PWD'):
$root = dirname(dirname(dirname(getenv('PWD'))));
if (file_exists($root.'/wp-load.php')) {
// WP 2.6
require_once($root.'/wp-load.php');
}
And change working directory before run this code:
cd /var/www/wp-content/themes/twenty_twelve/ && php script.php
If I were trying to solve this, I'd split __FILE__ along the path bits and create a SplFileInfo for each along the way, test with isDir and isLink, then try to determine how to handle reconstruction of the path once it's known to be different than expected so you can pull from the right directory. (If you're more of a procedural type, there's is_dir and is_link.)
That being said, I think you've already disqualified this solution. Maybe the tools are smart enough to do it for you. Try comparing the result of getRealPath to getPath? getRealPath expressly says that it resolves symlinks, while getPath doesn't expressly say that.
Even then, this sniffing might not be safe on client sites, depending on who the host is. I've seen some pretty creative shared hosting filesystem setups. You could add a check to php_uname and pull out the hostname of the machine, and if it isn't your dev box, don't do the extra work.
The PHP interpreter resolves symlinks before it processes them. You can do this yourself with the readlink function. PHP resolves the links because it's more efficient for *_once functions and code caches like APC, Xcache etc.
What you propably need is another way to find where a particular installation stores it's files. I'd recommend using {$_SERVER['DOCUMENT_ROOT']}/wp-content/wp-load.php assuming /share/www/portfolio is the document root.
None of the suggested solutions worked in all these environments, CLI, PHPUNIT, WebServer.
What I ended up with was defining a variable for my project's root.
define( 'ABSPATH', __DIR__ . '/' );
And then used it in my symlinked plugins.
This may not be what you can always do but if it's okay with your use case it definitely works everywhere!
eg:
require_once(ABSPATH .'/wp-load.php');
If you are using WordPress this is already defined and you can just use it!

How do I set an absolute include path in PHP?

In HTML, I can find a file starting from the web server's root folder by beginning the filepath with "/". Like:
/images/some_image.jpg
I can put that path in any file in any subdirectory, and it will point to the right image.
With PHP, I tried something similar:
include("/includes/header.php");
...but that doesn't work.
I think that that this page is saying that I can set include_path once and after that, it will be assumed. But I don't quite get the syntax. Both examples start with a period, and it says:
Using a . in the include path allows for relative includes as it means the current directory.
Relative includes are exactly what I don't want.
How do I make sure that all my includes point to the root/includes folder? (Bonus: what if I want to place that folder outside the public directory?)
Clarification
My development files are currently being served by XAMPP/Apache. Does that affect the absolute path? (I'm not sure yet what the production server will be.)
Update
I don't know what my problem was here. The include_path thing I referenced above was exactly what I was looking for, and the syntax isn't really confusing. I just tried it and it works great.
One thing that occurs to me is that some people may have thought that "/some/path" was an "absolute path" because they assumed the OS was Linux. This server is Windows, so an absolute path would have to start with the drive name.
Anyway, problem solved! :)
What I do is put a config.php file in my root directory. This file is included by all PHP files in my project. In that config.php file, I then do the following;
define( 'ROOT_DIR', dirname(__FILE__) );
Then in all files, I know what the root of my project is and can do stuff like this
require_once( ROOT_DIR.'/include/functions.php' );
Sorry, no bonus points for getting outside of the public directory ;) This also has the unfortunate side affect that you still need a relative path for finding config.php, but it makes the rest of your includes much easier.
One strategy
I don't know if this is the best way, but it has worked for me.
$root = $_SERVER['DOCUMENT_ROOT'];
include($root."/path/to/file.php");
The include_path setting works like $PATH in unix (there is a similar setting in Windows too).It contains multiple directory names, seperated by colons (:). When you include or require a file, these directories are searched in order, until a match is found or all directories are searched.
So, to make sure that your application always includes from your path if the file exists there, simply put your include dir first in the list of directories.
ini_set("include_path", "/your_include_path:".ini_get("include_path"));
This way, your include directory is searched first, and then the original search path (by default the current directory, and then PEAR). If you have no problem modifying include_path, then this is the solution for you.
There is nothing in include/require that prohibits you from using absolute an path.
so your example
include('/includes/header.php');
should work just fine. Assuming the path and file are corect and have the correct permissions set.
(and thereby allow you to include whatever file you like, in- or outside your document root)
This behaviour is however considered to be a possible security risk. Therefore, the system administrator can set the open_basedir directive.
This directive configures where you can include/require your files from and it might just be your problem.
Some control panels (plesk for example) set this directive to be the same as the document root by default.
as for the '.' syntax:
/home/username/public_html <- absolute path
public_html <- relative path
./public_html <- same as the path above
../username/public_html <- another relative path
However, I usually use a slightly different option:
require_once(__DIR__ . '/Factories/ViewFactory.php');
With this edition, you specify an absolute path, relative to the file that contains the require_once() statement.
Another option is to create a file in the $_SERVER['DOCUMENT_ROOT'] directory with the definition of your absolute path.
For example, if your $_SERVER['DOCUMENT_ROOT'] directory is
C:\wamp\www\
create a file (i.e. my_paths.php) containing this
<?php if(!defined('MY_ABS_PATH')) define('MY_ABS_PATH',$_SERVER['DOCUMENT_ROOT'].'MyProyect/')
Now you only need to include in every file inside your MyProyect folder this file (my_paths.php), so you can user MY_ABS_PATH as an absolute path for MyProject.
Not directly answering your question but something to remember:
When using includes with allow_url_include on in your ini beware that, when accessing sessions from included files, if from a script you include one file using an absolute file reference and then include a second file from on your local server using a url file reference that they have different variable scope and the same session will not be seen from both included files. The original session won't be seen from the url included file.
from: http://us2.php.net/manual/en/function.include.php#84052
hey all...i had a similar problem with my cms system.
i needed a hard path for some security aspects.
think the best way is like rob wrote. for quick an dirty coding
think this works also..:-)
<?php
$path = getcwd();
$myfile = "/test.inc.php";
/*
getcwd () points to:
/usr/srv/apache/htdocs/myworkingdir (as example)
echo ($path.$myfile);
would return...
/usr/srv/apache/htdocs/myworkingdir/test.inc.php
access outside your working directory is not allowed.
*/
includ_once ($path.$myfile);
//some code
?>
nice day
strtok
I follow Wordpress's example on this one. I go and define a root path, normally the document root, and then go define a bunch of other path's along with that (one for each of my class dirs. IE: database, users, html, etc). Often I will define the root path manually instead of relying on a server variable.
Example
if($_SERVER['SERVERNAME'] == "localhost")
{
define("ABS_PATH", "/path/to/upper/most/directory"); // Manual
}
else
{
define("ABS_PATH, dirname(__FILE__));
// This defines the path as the directory of the containing file, normally a config.php
}
// define other paths...
include(ABS_PATH."/mystuff.php");
Thanks - this is one of 2 links that com up if you google for php apache windows absolute path.
As a newbie to intermed PHP developer I didnt understand why absolute paths on apache windopws systems would be c:\xampp\htdocs (apache document root - XAMPP default) instead of /
thus if in http//localhost/myapp/subfolder1/subfolder2/myfile.php I wanted to include a file from http//localhost/myapp
I would need to specify it as:
include("c:\xampp\htdocs\myapp\includeme.php")
or
include("../../includeme.php")
AND NOT
include("/myapp/includeme.php")
I've come up with a single line of code to set at top of my every php script as to compensate:
<?php if(!$root) for($i=count(explode("/",$_SERVER["PHP_SELF"]));$i>2;$i--) $root .= "../"; ?>
By this building $root to bee "../" steps up in hierarchy from wherever the file is placed.
Whenever I want to include with an absolut path the line will be:
<?php include($root."some/include/directory/file.php"); ?>
I don't really like it, seems as an awkward way to solve it, but it seem to work whatever system php runs on and wherever the file is placed, making it system independent.
To reach files outside the web directory add some more ../ after $root, e.g. $root."../external/file.txt".

Categories