Get calling file name from include()

15,364

Solution 1

This is actually just a special case of what PHP templating engines do. Consider having this function:

function ScopedInclude($file, $params = array())
{
    extract($params);
    include $file;
}

Then A.php can include C.php like this:

<?php
// A.php
ScopedInclude('C.php', array('includerFile' => __FILE__));

Additionally, B.php can include C.php the same way without trouble.

<?php
// B.php
ScopedInclude('C.php', array('includerFile' => __FILE__));

C.php can know its includer by looking in the $params array.

<?php
// C.php
echo $includerFile;

Solution 2

So this question is pretty old, but I was looking for the answer and after leaving here unsatisfied, I came across $_SERVER['SCRIPT_FILENAME']; Of course this works if the php file doing the including is a web page.

This gives you the full path of the "including file" on the server. eg /var/www/index.php. so if you want just the filename, eg index.php, you will need to use basename() eg

basename($_SERVER['SCRIPT_FILENAME']);

So, if in your index.php you have the following line:

<?php include("./somephp.php"); ?>

and in somephp.php you have

echo "this is the file that included me: " . basename($_SERVER['SCRIPT_FILENAME']);

you will get

this is the file that included me: index.php

output to the browser. This also works if the user is accessing your file without explicitly including the filename in the url, eg www.example.com instead of www.example.com/index.php.

Solution 3

Solution

Knowing that the functions used to include files are include, include_once, require and require_once, also knowing that they are reserved words in PHP, meaning that it will not be possible to declare user functions with the same name, and based on wedgwood's idea of using debug_backtrace you can actually work out from what file the include was called.

What we are going to do is iterate over the backtrace untill we find the most recent call to any of the four include functions, and the the file where it was called. The following code demostrate the technique:

function GetIncludingFile()
{
    $file = false;
    $backtrace =  debug_backtrace();
    $include_functions = array('include', 'include_once', 'require', 'require_once');
    for ($index = 0; $index < count($backtrace); $index++)
    {
        $function = $backtrace[$index]['function'];
        if (in_array($function, $include_functions))
        {
            $file = $backtrace[$index]['file'];
            break;
        }
    }
    return $file;
}

The above code will return the absolute path of the file where the last include happened, if there hasn't been any include it will return false. Note that the file may has been include from a file that was included from another file, the above function only works for the deepest include.

With a simple modification, you can also get the last included file:

function GetIncludedFile()
{
    $file = false;
    $backtrace =  debug_backtrace();
    $include_functions = array('include', 'include_once', 'require', 'require_once');
    for ($index = 0; $index < count($backtrace); $index++)
    {
        $function = $backtrace[$index]['function'];
        if (in_array($function, $include_functions))
        {
            $file = $backtrace[$index - 1]['file'];
            break;
        }
    }
    return $file;
}

Observations

Note that __FILE__ is not the included file, but the current file. For example:

file A.php:

<?php
    function callme()
    {
        echo __FILE__;
    }
?>

file B.php:

<?php
    include('A.php');
    callme();
?>

file index.php:

<?php
    include('B.php');
?>

In the above example, in the context of the file B.php the included file is B.php (and the including file is index.php) but the output of the function callme is the path of A.php because __FILE__ is in A.php.


Also note that $_SERVER['SCRIPT_FILENAME'] will give the the absolute path to the script requested by the client. If $_SERVER['SCRIPT_FILENAME'] == __FILE__ it means that the current file is the requested one, and therefore there probably hasn't been any includes...

The above method checks if the current file is the requested one, but not if it hasn't been included (below is an example of how the requested file can be included). An actual solution to check if there has not been any inclusions could be to check count(get_included_files()) == 1.


The requested file can be an included file in the following way:

file x.php

<?php
   $var = 'something';
   include('index.php');
?>

file index.php

<?php
    if (!isset($var))
    {
        include('x.php');
        exit;
    }
    echo 'something';
?>

In the above example, the file index.php will include x.php, then x.php will include index.php back, afterwards index.php outputs "something" and returns control to x.php, x.php returns control to index.php and it reaches exit;

This shows that even if index.php was the requested script, it was also included from another script.

Solution 4

I can't find the easy way to cover this. But If the including one is really important to you, you could hack it with some global variable and your own include function.

e.g.

<?php                                                                            

    $g_including_files = array();                                                    

    function my_include($file) {                                                     
        $bt =  debug_backtrace();

        global $g_including_files;                                                   
        $g_including_files[basename($file)] = $bt[0]['file']; 
        return include($file);                                                                                                                                                                     
    } 

May that be helpful for you :)

Share:
15,364
Jon Egeland
Author by

Jon Egeland

I want to make people's work more enjoyable. I like projects that allow people to be more effective at what they do, to work more quickly, and to feel accomplished. I've been a developer in some regard for more than half of my life and want to continue applying what I've learned to new experiences. I don't like hype. I'm not ready to "take it to the next level". I don't care how much you raised in Series A. I want a company that's built on itself and its merit, not other people. I've never been good at keeping these up to date, so just follow me on GitHub for an idea of what I'm doing at any given time.

Updated on July 15, 2022

Comments

  • Jon Egeland
    Jon Egeland almost 2 years

    I want to get the name of the file that includes another file from inside the included file.

    I know there is the __FILE__ magic constant, but that doesn't help, since it returns the name of the included file, not the including one.

    Is there any way to do this? Or is it impossible due to the way PHP is interpreted?

  • Jon Egeland
    Jon Egeland about 12 years
    Thank you, this worked exactly how I needed it to. The reason I picked this over @wedgwood's is for the simplicity of expandability. Thanks again.
  • Theraot
    Theraot about 11 years
    you were on the right track... I have a working solution based on your idea (exactly one year after): stackoverflow.com/a/16125864/402022
  • Sz.
    Sz. over 5 years
    Googlers, don't miss @Theraot's fantastic solution, which doesn't require any change in the including script!
  • MrMesees
    MrMesees over 4 years
    It would be awesome to get more info on the debug_backtrace() and it's performance characteristics.
  • Theraot
    Theraot over 4 years
    @MrMesees You can have a look at the PHP source. It appears to have linear complexity with respect to the depth of the call stack, and populating objects and argument have extra cost.
  • BeNice
    BeNice over 4 years
    F***** genius! I could only upvote by one but please feel like I have given u ten. Just PERFECT!!!!!! (Sorry getting a bit over excited here!)
  • BeNice
    BeNice over 4 years
    @Sz. Thanks for the stear. EXACTLY what I was looking for. Does PHP 7 introduce a simpler way of doing this? I would have thought this would be useful in all sorts of places.