Sign in to follow this  

[web] PHP: Including files messes up relative paths

This topic is 4223 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a file structure that looks like this:
/
  login.php
  db.php
  /agentadmin
    getagentid.php
getagentid.php has the following line: include('../db.php'); login.php has the following line: include('agentadmin/getagentid.php'); The problem here is that login.php includes getagentid.php, which tries to include ../db.php, which doesn't exist relative to login.php (but does exist relative to getagentid.php). Absolute paths are unfortunately out of the question. How do I make include()'s work from the current file's path?

Share this post


Link to post
Share on other sites
I simply set a $root_path variable at the top of my entry files (that is, any file that can be called directly in a browser). E.g:


if (!isset($root_path))
$root_path = './';


./ would be for your login.php. For your getagentid.php it would be ../ instead (if you can call that file directly. If it's only ever included the you don't set it at all - it will be inherited from the entry file). Then, simply include everything relative to that:


include($root_path . 'db.php');


Ofcourse, if all your entry files are in the top directory then you can simply set all the includes manually. E.g. include('db.php'); without the ../ in getagentid.php.

Share this post


Link to post
Share on other sites
Yeah, this annoys me too...I don't know if this is considered to be "by design" or not, but I would prefer that it's relative to the file containing the reference.

Share this post


Link to post
Share on other sites
Rule #1: Create an include folder for all utility files.
Rule #2: Adjust the php include_path variable to include that first.

Result: No need to use relative paths anymore.

Suggestion: Have a master "common.inc.php" file that loads the framework, initializes the db, etc.

Using such a mix of relative paths is really bad design. It's okay (although I don't like it) to include a file that's in a subfolder, but going back up the chain is not good.

So:

.htaccess:
php_value include_path /var/www/html/include:/usr/share/pear: ... etc

Structure:
/login.php
/include/common.inc.php
/include/db.inc.php
/include/agentadmin/getagentid.php

login.php:

<?php
require_once('common.inc.php');
require_once('agentadmin/getagentid.php');

// profit
?>



common.inc.php:

<?php
require_once('db.inc.php');
// whatever needs to be set up 80%+ of the time
?>



I'm not sure what "agentadmin/getagentid.php" is. If it's a content page, then I wouldn't necessarily put it into the include file. That is, if your login.php acts as some sort of dispatcher (which I don't quite follow) then I'd not consider the "getagentid.php" as a utility include. If the "getagentbyid.php" is simply a set of functions, then I'd put it in an include folder.

Lastly, I highly recommend using PHP classes as namespaces around similar sets of function and enabling autoloading of classes. For instance:


<?php
require_once('common.inc.php');

Agent::getId(); // automatically loads class/Agent.class.php upon first access

// this is assuming that Agent::getId() is a static function, etc.
?>



Agent.class.php

<?php
class Agent
{
public static function getId()
{
return "Whatever";
}
}
?>



The small performance hit is well worth your sanity as you no longer have to specify any include files, except for your global one. (And that can be eliminated if you use the auto include directive...)

Share this post


Link to post
Share on other sites
You can actually get around this entirely in PHP. What I do is have a function which modifies the include path - put this in an include which *DOES* have a relative path.


// Set our include path to add the document root.

function FixIncludePath()
{
$include_path = ini_get("include_path");
$docroot = @ $_SERVER['DOCUMENT_ROOT'];
if ($docroot) {
ini_set("include_path", $include_path . ":" . $docroot);
} else {
// Doc root not set, use .. instead (This assumes that the CWD is here when run from CLI)
ini_set("include_path", $include_path . ":..");
}
}


Then after that's done, all further includes can be relative to the web root.

Hence, the app contains no absolute paths, but includes still work as expected.

Mark

Share this post


Link to post
Share on other sites
The best way to solve the problem is to use __FILE__ constant.
The BeanDog's example can be written like this:

In getagentid.php write:
require_once dirname(dirname(__FILE__)).'/db.php';

In login.php:
require_once dirname(__FILE__).'/agentadmin/getagentid.php';

The $_SERVER['DOCUMENT_ROOT'] aproach also works fine in web enviroment, but fails when running php file as console script.
And it also has the drawback that you should know all paths relative to DocumentRoot, which is a problem for redistributable code since user probably would like to place it under his own folder.

Share this post


Link to post
Share on other sites
No, using __FILE__ can be a very neat solution.

Specifically, you could use a mixture of my method and pash_ka's one, where you put dirname(__FILE__) into the include_path anyway.

You'd only want to do that once.

Mark

Share this post


Link to post
Share on other sites
Quote:
That is horrible. PHP has "include_path" for a reason.

Yes, and changing include_path may be usable for some tasks, like including PEAR libraries.
(By the way, there is one more way to do it. I usualy have line like this:
Quote:
set_include_path(dirname(__FILE__).'/pear'.PATH_SEPARATOR.get_include_path());

)
But there is also method used for Smarty (SMARTY_DIR): define a constant for your application root directory and use it as prefix for relative paths.

Share this post


Link to post
Share on other sites
Quote:
Specifically, you could use a mixture of my method and pash_ka's one, where you put dirname(__FILE__) into the include_path anyway.

I can live with an include_path that gets dynamically set, but there's a better way: include('settings.inc.php');. Inside there, set the include path to a hard coded path, set a few variables, define whatever constants you need, etc.

It's really quite pointless to take the performance hit* of dirname(__FILE__) on every page, when you can just take 15 seconds and set it forever. With the settings file, you can easily upload files from development to production without worrying about changing important things. (Just remember never to upload the settings file!)

* I realize it's quite small in the grand scheme of things, but it's pointless nonetheless. ;) It's really more of something like: if you cannot come up with a non-hacky way of including a file, what's the rest of the code look like?

There is absolutely no excuse, however, of sticking it in every include() directive. That just shows ignorance of the PHP language. If one is indifferent after knowning the proper way, then I'm sure the rest of his code is a pile of spaghetti that no one cares to touch.

Quote:
But there is also method used for Smarty (SMARTY_DIR): define a constant for your application root directory and use it as prefix for relative paths.

That is almost as bad as the dirname(__FILE__) hack. When PHP offers standard functionality to do something, don't hack around it for no reason (other than ignorance). It's no harder to tell someone to adjust ini_set(), then it is to adjust the SMARTY_DIR constant.

Share this post


Link to post
Share on other sites
This is pointless discussion.
I agree that using one 'settings.inc.php' is a good way for many tasks. I'm using something like it myself, by the way.
But the point of this topik is to show various methods to include files. :)
Originaly, i've just wrote about __FILE__ constant and showed how to use it in BeanDog's example. Without any refactoring.

And i should say, what is good for one may be bad for other. Changing include path may be good for application, but bad for library (because of possible naming conflicts).
Also think of forum, which has 'settings.inc.php' and CMS which use this forum by including some files from it. If both change include_path than someone will have troubles.

Share this post


Link to post
Share on other sites
I do agree that special care has to be taken for modules meant to be reused on different websites. I just meant the fixed directory is not necessary in general, but I think it is acceptable for a reusable library like Smarty to use out of convenience.

Share this post


Link to post
Share on other sites

This topic is 4223 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this