2

Suppose I have a PHP file that does important things.

doAllTheThings.php:

<?php
    include '../importantThing1.php';
    include '../importantThing2.php';
    include '../importantThing3.php';
    //lots more complicated code below

Let's also suppose I type the address into the web browser, it takes half a minute but all the things get done, known good, fantastic, let's not mess with it. (There's lots of valid reasons to not mess with it. Maybe I didn't write it and don't understand it. Maybe I'm not given access to it. Maybe this is an emergency patch, and don't have time to update and test every single include path.)

Now suppose I want the user to be able to do something that can cause the code to run.

customerInterface.php:

<?php
    //lots more complicated code above
    if(doTheThings){
        include '../../things/important/doAllTheThings.php';
    }

It runs, but now the relative paths don't work. The importantThing links are broken. No important things get done.

How do I execute "doAllTheThings.php" such that it will behave the same as if I typed its address directly into the browser address bar? (Without changing the directory structure, the file location, or "doAllTheThings.php")

9
  • 1
    The best bet is to use absolute paths instead relative paths, you can avoid a lot of issues like this one. Commented Dec 30, 2019 at 3:52
  • I know how to do this through javascript using AJAX, but no way should the best solution trust the client computer. Commented Dec 30, 2019 at 5:12
  • IMHO Disagree about absolute paths... hard coding is a real headache to maintain. For your question see stackoverflow.com/questions/9997391/… Commented Dec 30, 2019 at 5:26
  • @TimMorton yeah I was googling this issue before posting and found some quite fine alternatives between paths being completely absolute or completely relative. Still, I like being able to test a change from the address bar without having to hunt down every single instance to check that it behaves the same way from each of them. All solutions around this problem seemed difficult to maintain and test. Commented Dec 30, 2019 at 6:32
  • Test from the address bar? Are you trying to run a CLI through your browser by typing in the absolute path? That’s a really cumbersome way to do it. I test directly from my editor. (Unit testing with phpunit, that is). But I can also run CLI programs directly from the editor too. Commented Dec 30, 2019 at 12:48

1 Answer 1

6
+50

Presuming there is no client-side code being run in doAllTheThings.php and you know the path to doAllTheThings.php.

The main issue is how include resolves paths within nested include files.

If the file isn't found in the include_path, include will finally check in the calling script's own directory and the current working directory before failing. [sic]

In this case the calling script is customInterface.php.

Being that include resolves relative paths from the current working directory of the executed script file, the simplest approach to circumvent the issue and make the calling script behave as if you executed doAllTheThings.php directly, you can use chdir in order to change the current working directory.

chdir https://3v4l.org/D0Tn6

<?php
    //...

    if (true) {
        //check to make sure doAllTheThings.php actually exists and retrieve the absolute path
        if (!$doAllThings = realpath('../../things/important/doAllTheThings.php')) {
           throw new \RuntimeException('Unable to find do all things');
        }

        //change working directory to the same as doAllTheThings.php
        chdir(dirname($doAllThings));

        //include doAllTheThings from the new current working directory
        include $doAllThings;

        //change the working directory back to this file's directory
        chdir(__DIR__);
    }

    //...
?>

include __DIR__ or dirname(__FILE__)

However, if possible, I strongly recommend using absolute paths by including the root path, by appending __DIR__ or dirname(__FILE__) in PHP < 5.3 to any relative include paths. This will absolve the need to use chdir() as a workaround, and allow PHP to resolve the correct paths to include, while also allowing the application as a whole to function on any system the scripts are executed from.

<?php
    include __DIR__ . '/../importantThing1.php';
    include __DIR__ . '/../importantThing2.php';
    include __DIR__ . '/../importantThing3.php';

set_include_path

Another more complex approach is to specify the include file directories, by using set_include_path(). However, if you are not aware of the directory the nested include scripts are in, this would require you to parse the include file to check for them. As such I do not recommend this approach, albeit viable.

<?php
if ($doAllThings = realpath('../../things/important/doAllTheThings.php') {
    //retrieve the directory names of the scripts to be included
    $basePath = dirname($doAllThings);
    $subPath = dirname($basePath);

    //add the directories to the include path
    $include_path = set_include_path(get_include_path() . PATH_SEPARATOR . $basePath . PATH_SEPARATOR . $subPath);

    include $doAllThings;

    //restore the include path
    set_include_path($include_path);
}
Sign up to request clarification or add additional context in comments.

1 Comment

Good answer. I'll wait a few more days before choosing the best one, but this is definitely the correct answer if no one else answers. (I'm assuming. I haven't tested it yet.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.