tags:

views:

1392

answers:

12

I'm in the process of setting up a php project, but am not very familiar with how to properly use php's include/require commands. My layout currently looks like this:

/public --apache points into this directory
/public/index.php
/public/blah/page.php
/utils/util1.php -- useful classes/code are stored in other directories out here
/dbaccess/db1.php

dbaccess/db1.php

require '../utils/util1.php

public/index.php

require '../dbaccess/db1.php'

public/blah/page.php

require '../../dbaccess/db1.php'

The problem is this from the php 'include' documentation:

If filename begins with ./ or ../, it is looked only in the current working directory

So public/blah/page.php fails because it includes dbaccess/db1.php which blows up when it tries to include util1.php. It fails because it's relative path is from the original script in public/blah/, not from dbaccess/

This seems pretty stupid -- db1.php has to just know where it's being included from which isn't going to work.

I've seen strategies like this:

require_once dirname(__FILE__) . '/../utils/util1.php');

That apparently works since now the path is an absolute path, but just seems really bizarre to me.

Is that normal? Should I continue down that path or am I missing something obvious here?

A: 

Why not require it based on it's full path?

For example, /sharedhost/yourdomain.com/apache/www is your document root, so why not use

require('/sharedhost/yourdomain.com/apache/www/dbutils.php');

This also has the advantage of you being able to store your includes outside of your wwwroot so they are far less likely to be inadvertenly exposed via the web.

You could also set up a global variable equal to the /sharedhost/yourdomain.com/apache/ part of it so you can move the site around.

require(WWWROOT . '/dbutils.php');
Rob Burke
There won't be a constant document root. This will be on many different developer machines, on a live site, on an internal testing area, etc.So where would I set up that global variable?
Clyde
You don't want the code to make assumptions about where it is in the system's filesystem.
staticsan
@Clyde, surely it can be set in a config file as and when you deploy it on each machine?@staticsan, again, something that is remedied by defining WWWROOT just once during the deployment / install phase?
Rob Burke
+4  A: 

Have a configuration script that sets the "INSTALL ROOT" of your project and then use absolute paths. Relative path with multiple includes is a headache in php.

DEFINE("INSTALL_ROOT", "/path/to/www/project")

require_once(INSTALL_ROOT . '/util1.php')

grepsedawk
Where would I set that global define? I obviously don't want to do that in every file.
Clyde
Clyde: in a configuration script, like the text suggests.
OIS
Constants must be defined using a string as the name. That code will look like it works if you turn off error reporting but only because if PHP can't find a constant to match that string literal it'll just dump it as a string. `define('INSTALL_ROOT', '/path/to/dir/');` is correct.
dcousineau
Updated on dcousineaus comment. It wasn't really meant to be drop in code :)
grepsedawk
It's best not to confuse :P
dcousineau
A: 

I use

require '../path/to/file.ext';

without problems.

Also require is a statement not a function so it should be used as

require '/path/to/file.ext';

not

require('/path/to/file.ext');
Unkwntech
your second statement is just not true.
smazurov
require and include are actually directives. They just happen to look and work (a lot) like statements. And the bracket-less version looks better as it has less visual pollution.
staticsan
it most certainly is: "The require() statement includes and evaluates the specific file." From: http://us2.php.net/require
Unkwntech
though the brackets aren't required, it doesn't hurt more than maybe 1 or 2 processor ticks.
nickf
I know that it is largely semantics, I was just pointing it out, I don't think it warranted-2, but maybe I'm biased.
Unkwntech
A: 

Keep in mind, it starts in the current working directory and then looks through the include paths. If you want to reference all of your paths from some central root directory (or many) you can add that directory in the php.ini file or you can do it programatically with set_include_path( $path.PATH_SEPERATOR.get_include_path());

ehassler
A: 

I suggest an abstraction strategy.

  1. In your application page area, have a single file that all pages include.

  2. This "local" include file has one job: find the include file that is outside the application page area. It then includes that. It can probably be as simple as <?php include dirname(__FILE__).'/../include/include.php/'; ?>

  3. This second file is the single entry point into your library structure. It, or something else it includes, has the job of finding where everything is and including it.

This structure means you have just one file as your library's entry point and how it finds the rest of the library is not the application pages' problem. It also means you have just one file in your application area which knows how to find your library's entry point.

If you need a way for different application pages to load different things, I would suggest a modularisation approach. This can either be a global array you set before the master include, or a function you can call to request libraries by name. Yes, this is a slightly fancier way of your master library file declaring a constant of where everything is -- but it removes the temptation of doing include LIBRARY_DIR.'/utils/util.php'; which straightaway makes it unnecessarily difficult to move util.php out of utils and into misc/util at a later date.

The other advantage of the chained files is that it is then much easier to make your codebase relocatable, which makes it possible for multiple versions to be runnable. And it makes it possible to have one tree for the application and another for your library. Which means another application could use your library. In fact, you could extend the chaining a bit more if you want to help with the isolation further.

staticsan
+1  A: 

in my config / setup file, i do something like

define('MYAPP_BASEDIR',realpath('.'));

then i reference everything relative to that.

... if your include directory relates specifically to class files and you are able to name them so that the include file name could be derived from the class, you might like to look into spl_autoload_register().

this latter part isn't a direct answer to your question, but it's very handy if you're doing includes for each class you use.

Chris Burgess
+8  A: 

Usually, the standard conventions are thus: like @grepsedawk said, you'll want to define a constant that contains the root of your project folder and if you can the root of your includes folder:

define('APP_ROOT', dirname(__FILE__));
define('INCLUDE_ROOT', APP_ROOT . "/includes");

Note: the constant name needs to be a string!

Also, you'll notice I'm using dirname(__FILE__);. If you place your constants definition file in a subdirectory, you can do a dirname(dirname(__FILE__));, which is the equivalent of a ../.

Now some other caveats. While PATH_SEPARATOR is a cool constant, it is not needed. Windows accepts / or \ in path names, and since Linux only users / as a path separator, go ahead and always use a / instead of mucking up your code with repeated references to PATH_SEPARATOR.

Now that you have your root constants defined, what you'll do when you need a configuration file included is a simple:

include INCLUDE_ROOT . '/path/to/some/file.php';

You'll probably want your constant definitions (the define(...)'s above) in a bootstrap script in your root directory:

www_root/
  index.php
  bootstrap.php

The bootstrap will contain the defines (or an include of the constants file), as well as an include of any files that will be required by EVERY page.

And finally the last standard convention you may not use, but if you start doing object oriented programming, the most common method (the PEAR standard) is to name your classes by using an _ to separate namespaces:

class GlobalNamespace_Namespace_Class
//...

And then organizing your file structure mapping name spaces to subdirectories (literally replacing all _'s with /'s):

include_dir/
  GlobalNamespace/
   Namespace/
    Class.php

And using __autoload() functions to load your classes, but that's another question.

dcousineau
And I forgot to mention, using dirname(__FILE__) gives you the freedom of using full paths (no need to worry about a ./ or ../ resolving) without having to maintain the full paths as you move between deployment servers (e.g. "oops copied /home/httpd/.../ to live when it should be /var/www/.../")
dcousineau
A: 

You're right, your scripts doesn't have to know the physical path where your includes are.

IMO the location where the includes are should be configured in the PHP.INI file (or .htaccess if you preffer).

Suponse your includes (utils and database are stored here /home/scott/php_includes/).

PHP.INI:

include_path=.:/home/scott/php_includes/

Now your scripts can include the libraries in this way:

dbaccess/db1.php:

require_once 'utils/util1.php';

public/index.php

require_once 'dbaccess/db1.php';

public/blah/page.php:

require_once 'dbaccess/db1.php';

Gravstar
A: 

A lot of people have provided good solutions, but I've just got one performance-related remark while talking about includes and requires.

If you start include'ing and require'ing a lot of files, it can be tempting to use include_once or require_once. Cachegrinds of scripts that use a lot of _once's have shown that they really slow down performance as the script has to stop what its doing to scan and make sure the file hasn't been included already. Eliminating as many of the _once's as you can will help a lot.

Bob Somers
A: 

There was perfect solution - pecl extension called "pwee" - it allowed user define his/her own extern superglobal constants / variable using XML file. Thus you were able to use absolute path as I recomend in such form:

require_once APP_ROOT."/path/to/your/script.php";

The advantage of such solution was:

  • accessible from everywhere
  • no server load - everything in server memory

The XML file contained

  <Environments>
    <Application name="www.domain.com" namespace="">
      <Constants>
        <Constant name="APP_ROOT" value="/full/path/to/project/source" />
      </Constants>
      <Constants>
        <Constant name="WEB_ROOT" value="/full/path/to/project/public" />
      </Constants>
    </Application>
  </Environments>

link to pwee project

You should distinguish these cases of inclusion:

  • standalone library - all includes should be relative - to let user integrate it to his/her project easily
  • executable scripts in your public directory - contain absolute includes to project files and to standalone libraries public files (which has relative includes inside - transparent to user). Using the APP_ROOT constant is elegant way.
  • a,link,script, form html elemets and header forwards should use relative path when diving into tree hierarchy and absolute path when using common files from higher levels of hierarchy

In case of relative path use this form:

  require_once "./relative/path/to/script.php";

Why do I use past tense? Because the project is not more supported - works only with Php4. If anyone knows similar solution with support, please let me know.

A: 

The best way to do that is to build flexible autoload system.

Simple map of classnames and proprietary patches. Then any internal require_* or include_* is not needed.

There is of course matter of relative/absolute path for autoloader. Well, absolute is most system-efficient, so in the array I mentioned before u can prepend some kind of variable {I have used Phing-style variable} e.g.

<map>
    <path classname="MyClass">${project_directory}/libs/my_classes/MyClass.php</path>
    <path classname="OtherClass">${project_directory}/libs/some_new/Other.php</path>
    <!-- its so flexible that even external libraries fit in -->
    <path classname="Propel">${project_directory}/vendors/propel/Propel.php</path>
    <!-- etc -->
</map>

This is xml (think about ini or yaml as well) file and requires compile to php during first start, but after that any path is absolute.

Oh, as you can see no file naming convention or file layout is mandatory - its huge advantage.

Cheers, Alan.

Alan Bem
A: 

I see this is a year old, but I'm a new guy and found it while asking the same question. It seems that every time I move my simple scripts from one server to another I have to redefine where things are. Meh! I'm tinkering, like I said, I'm new... I don't know anything much. I set up a test environment at home, built a few things, and deployed them to a shared host. The result was that the $_server['DOCUMENT_ROOT'] was two folders higher than the public_html folder on one server, and on another server it was one folder higher. That skewed all my references. So I tried $_server['WEB_ROOT'] and failed again. I was thinking that the web root was a reference to the root of publicly accessible folders on the server, but I was wrong. Meh! Anyway, all these fancy tricks look like they'll be a lot of fun to tinker with. I have one to throw in the pile that did something really simple without adding a lot of code I don't understand (I don't understand a lot of this code either, I just kept adding as I read rules and got it to work).

Umm, I should get to it anyway. This gave me a reference to the publicly accessible web root on all three servers I tried it on. Now I can move my folder anywhere and it just works, no config file necessary XD!! No more ../ or ../../ whatever, I get to reference things like when I'm writing HTML and CSS. XD! If it works everywhere I'll be happy, if not I'll figure out some other way... maybe.

Tell me what you think XD!

pub-doc-root.php

<?php

//You only need to paste the following line into your script once,
//and it must come before you reference the public document root of your website.
//Use $pubroot.'/path_from_public_document_root_to_file/filename.php

$pubroot = (str_replace(($_SERVER['PHP_SELF']), '', (str_replace('\\', '/', (realpath(basename(getenv("SCRIPT_NAME"))))))));


//uncomment the next line to show the calculated public document root
//relative to the document root.

//echo ("$pubroot");
?>

My test environment:
php 5.3.1
Apache 2.2.14 (Win32) mod_ssl 2.2.14 OpenSSL 0.9.8k
ZendServer-CE-5.0.0GA_RC181-5.3.1-Windows_x86

I don't know all the specs on the servers I've deployed to, and I already sound like an idiot with this post's length. :P

Kastor