The PATH_TRANSLATED blues

Wondering if anyone can provide some help / tips for a problem we’ve encountered in WACT. Can imagine other people are having the same problem.

Basically we need a way to determine that path of the script where execution began from within WACT.

Typically when using WACT you’d begin a script like;


// myscript.php
require_once '/path/to/wact/common.inc.php';

// etc...
?>

Code within WACT then needs to be able to determine where myscript.php is located in the filesystem, so that a users templates, for example, can be located relative to myscript.php’s path.

This may seem like a simple problem to solve, by simply using;


$myscript_path = dirname($_SERVER['SCRIPT_FILENAME']);

Unfortunately this only works on some PHP configurations – when PHP is used as a CGI executable (and probably the same for the CLI variant) contains the path to the PHP executable, not the PHP script where execution began.

An alternative option which seemed to look good for a short time was;


$myscript_path = dirname($_SERVER['PATH_TRANSLATED']);

Looking at the PHP bugs list though, there seem to have been issues with this variable, relating to the Apache version being used. Worse news is it’s been completely dropped from PHP5, mentioned here.

Any other ideas for how to reliably determine the path of the script where execution began? Beginning to wonder if PHP needs a predefined constant such as __MAIN__ which handles this independent of PHP install.

Jeff highlights the problem in more detail here.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • cyngon

    Just wondering: Are either of those techniques affected if you use mod_rewrite and the address they types into the browser isn’t necessarily the address they end up at?

  • Matt Wilson

    I use mod_rewrite heavily, which has a similar effect in that you can’t trust pathnames (although $_SERVER[PHP_SELF] may still work).

    Anyway, my solution is to define a set of variables regarding paths (normally http path and file path, sometimes https), so the only known path necessary is the core included file; everything can use absolute paths using the core include’s defined vars. Example:
    $path['http'] = /var/www/sitex;
    include($path['http'].’/includes/x.php’);

  • http://thatwebthing.com KillAllDash9

    It’s not super elegant, but you could start every script with:

    if (!defined(‘MAIN_EXEC_DIR’)) define(‘MAIN_EXEC_DIR’, dirname(__FILE__));

    Then MAIN_EXEC_DIR would always contain the information you are looking for. Certainly, it’s a pain to have to put this in every file, though.

    Another option is to actually make use of the include_path INI setting, which can also be altered at run time via ini_set().

  • http://gosu.pl cagrET

    I don’t have php as cgi so cannot test it, but maybe this will work (only PHP 4 >= 4.3.0, PHP 5):


    < ?php
    // common.inc.php

    if (function_exists('debug_backtrace')) {
    $tmp = debug_backtrace();
    $_SERVER['PATH_TRANSLATED'] = $tmp[0]['file'];
    }
    ?>

  • http://gosu.pl cagrET

    Code within WACT then needs to be able to determine where myscript.php is located in the filesystem, so that a users templates, for example, can be located relative to myscript.php’s path.

    Btw.
    Why do you need $_SERVER['PATH_TRANSLATED'] for this ?
    If the script is called like this: http://domain.foo/some/myscript.php, template is here: /some/templates/example.tpl, relative path to template: ‘templates/example.tpl’, why can’t you just do: require ‘templates/example.tpl’ ?

    Or maybe this:

    require $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']) . “/templates/example.tpl”;

  • Demian Turner

    Perhaps this is helpful – Seagull dynamically determines the the script location on the filesystem and the web root of the calling URL. To do this each page execution first loads an init file:

    http://muse23.com/cgi-bin/cvsweb.cgi/seagull/init.php?rev=1.84&content-type=text/x-cvsweb-markup

    cheers

    Demian

  • http://www.phppatterns.com HarryF

    [QUOTE=cyngon]Just wondering: Are either of those techniques affected if you use mod_rewrite and the address they types into the browser isn’t necessarily the address they end up at?[/QUOTE]

    Good question. Not sure. Will explore that shortly.

  • http://www.phppatterns.com HarryF


    if (!defined('MAIN_EXEC_DIR')) define('MAIN_EXEC_DIR', dirname(__FILE__));

    I guess that will be the last resort. Would be a shame because it requires WACT users to define it (nice when it could happen automatically).


    < ?php
    // common.inc.php

    if (function_exists('debug_backtrace')) {
    $tmp = debug_backtrace();
    $_SERVER['PATH_TRANSLATED'] = $tmp[0]['file'];
    }
    ?>

    Good point. OK it’s PHP 4.3.0 only but perhaps we can use that inside some code which makes various guesses (using SCRIPT_FILENAME, PATH_TRANSLATED and any thing that might be useable).

    why can’t you just do: require ‘templates/example.tpl’ ?

    That may be an option to explore. We need the path for various file operations (e.g. writing a “compiled” template script) but fopen also supports using the include_path – good point.

    $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF'])

    Possibly – think those are also somewhat subject to the PHP SAPI you’re using but may be useful if we’re doing some sort of detection.

  • Davey

    whilst I was discussing the need for a __MAIN__ constant with Lukas Smith, Ilia chimed in with this little tid-bit:

    array_shift(get_included_files());

    Should work in any PHP version 4.0.0 up :)

    - Davey

  • Davey

    Whilst discussing the need for a __MAIN__ constant with Lukas Smith on IRC, Ilia chimed in with this little tid-bit:

    array_shift(get_included_files());

    Yay Ilia! This should work in PHP 4.0.0+ :)

    - Davey

  • Caramdir

    How about getcwd()? This returns the current working directory, and unlesse the user somewhere used chdir(), the cwd is the dir of the main entry point.

  • http://www.phppatterns.com HarryF


    array_shift(get_included_files());

    Phew! Nice one Davey – many thanks.

    PS: __MAIN__ would still be cool.

  • Davey

    I know that Ilia is pretty opposed to a __MAIN__ constant… so I guess the only way to do this is:


    if (!defined('__MAIN__')) {
    define('__MAIN__', array_shift(get_included_files()));
    }

    - Davey

  • hurst

    I have used the array_shift(get_included_files()) technique successfully until now. On a server with PHP version 4.1.2 using apache with mod_php, the main script was not in this array.
    There is a bug where someone is complaining about the main file being in the array: http://bugs.php.net/bug.php?id=25658.
    So unfortunately, this will not always work.

  • Rajesh

    I know at least one place where a programmer has used
    eval(file_get_contents(‘some_script.php’)) to include a file.
    I don’t exactly remember why but it solved a nice security
    problem.

    The array_shift(get_included_files()) won’t work in this case
    either.

  • DW

    getcwd() is the solution I chose :)