65

Is there any elegant way to check if a file was included by using include/include_once/require/require_once or if the page was actually loaded directly? I'm trying to set up a testing file inside class files while I'm creating them.

I'm looking for something similar to Python's if __name__ == "__main__": technique. Without setting globals or constants.

Federico J.
  • 15,388
  • 6
  • 32
  • 51
qwertymk
  • 34,200
  • 28
  • 121
  • 184
  • Think have found a way .... hold on – Baba Oct 21 '12 at 16:36
  • Duplicate of http://stackoverflow.com/questions/2397004/php-check-if-a-file-is-loaded-directly-instead-of-including – Cees Timmerman Apr 22 '14 at 17:19
  • 1
    This question is a bit ambiguous. if you want to know if a **different** file (from the current file) was already loaded: `if(in_array(__DIR__ . '/path/to/file.php', get_included_files())){ echo __DIR__ . '/path/to/file.php was already loaded'; }` – Arye Eidelman Dec 24 '20 at 18:12

11 Answers11

43

Quoted from: How to know if php script is called via require_once()?

I was looking for a way to determine if a file have been included or called directly, all from within the file. At some point in my quest I passed through this thread. Checking various other threads on this and other sites and pages from the PHP manual I got enlightened and came up with this piece of code:

if (basename(__FILE__) == basename($_SERVER["SCRIPT_FILENAME"])) {
  echo "called directly";
} else {
  echo "included/required";
}

In essence it compares if the name of the current file (the one that could be included) is the same as the file that is beeing executed.

Credit: @Interwebs Cowboy

Joshua Kaiser
  • 1,461
  • 9
  • 17
  • 13
    I think this breaks when both files have the same name but are in separate directories. Works fine without `basename()`. If `/var/www/index.php` includes `/var/www/test/index.php` for example, `/var/www/test/index.php` would think it is called directly. Still, +1 because this helped me alot :) – ChrisF Sep 19 '13 at 21:56
  • 1
    And it doesn't work when the file is called by a cron job because you don't have $_SERVER variable... – Tarik Aug 19 '17 at 14:01
  • Only voting this down because NullPoiиteя`s answer is better and should be accepted IMO. – michaelmcandrew Feb 25 '20 at 13:55
29

you can do this by get_included_files — Returns an array with the names of included or required files and validate against __FILE__

Community
  • 1
  • 1
NullPoiиteя
  • 56,591
  • 22
  • 125
  • 143
  • And how would I check that against what I have loaded? `/test.php` can include `/includes/test.php` – qwertymk Oct 21 '12 at 16:28
  • 1
    **Complete example here: https://stackoverflow.com/a/52467334/2377343** – T.Todua Sep 23 '18 at 14:59
  • 1
    Does not work for me. As the documentation on get_included_files says: The script originally called is considered an "included file," so it will be listed together with the files referenced by include and family. – scravy Sep 19 '20 at 01:25
18

I appreciate all the answers, but I didn't want to use any one's solution here, so I combined your ideas and got this:

<?php
    // place this at the top of the file
    if (count(get_included_files()) == 1) define ('TEST_SUITE', __FILE__);

    // now I can even include bootstrap which will include other
    // files with similar setups
    require_once '../bootstrap.php'

    // code ...
    class Bar {
        ...
    }
    // code ...

    if (defined('TEST_SUITE') && TEST_SUITE == __FILE__) {
        // run test suite here  
    }
?>
Angel Politis
  • 10,955
  • 14
  • 48
  • 66
qwertymk
  • 34,200
  • 28
  • 121
  • 184
14
if (defined('FLAG_FROM_A_PARENT'))
// Works in all scenarios but I personally dislike this

if (__FILE__ == get_included_files()[0])
// Doesn't work with PHP prepend unless calling [1] instead.

if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
// May break on Windows due to mixed DIRECTORY_SEPARATOR

if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']))
// Doesn't work with files with the same basename but different paths

if (realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME']))
// Seems to do the trick as long as document root is properly configured

Note: On WAMP Servers virtual-hosts sometimes inherit the default document root setting, causing $_SERVER['DOCUMENT_ROOT'] to display wrong path.

tim
  • 2,530
  • 3
  • 26
  • 45
6
<?php
    if (__FILE__ == $_SERVER['SCRIPT_FILENAME'])
    {
        //file was navigated to directly
    }
?>

Taken from mgutt's answer to a slightly different question here. It's important to note this doesn't work if the script is run from command line but other than that it functions exactly like python's

if __name__ == '__main__':

as far as I can tell

Community
  • 1
  • 1
Josh R
  • 145
  • 3
  • 8
6

get_included_files() return array where 0 index mean first "included" file. Because direct run mean "include" in this terms, you can simple check first index for equality for __FILE__:

if(get_included_files()[0] == __FILE__){
    do_stuff();
}

This can not work on PHP 4, because PHP 4 not add run file in this array.

Enyby
  • 4,162
  • 2
  • 33
  • 42
ngmh
  • 155
  • 1
  • 6
  • 2
    While this may solve the problem, code only answers are not encouraged on SO. It is always better to leave an explanation as to why this will solve the problem. – Vivz Sep 09 '17 at 13:01
  • 1
    Your answer should explain how this solves the problem and how this improves on the existing up-voted answers that provide the same solution. Also, as this question is 5 years old and has many upvoted answers, your efforts would be more appreciated by users who have recent unanswered questions. – FluffyKitten Sep 10 '17 at 07:55
  • Despite the negative comments, I found this the best answer to the question (doesn't assume web server, doesn't loop needlessly, simple, etc). Thanks Enyby and ngmh. – Iiridayn May 21 '20 at 07:48
5

They is no way to separate them as include/include_once/require/require_once but php has get_included_files and get_required_files which is the same thing and only returns array of all included files. Its does not separate it if its required or included.

Example a.php

include 'b.php';
include_once 'c.php';
require 'd.php';
var_dump(get_required_files());

Output

array
  0 => string '..\lab\stockoverflow\a.php' (length=46) <---- Returns current file
  1 => string '..\lab\stockoverflow\b.php' (length=46)
  2 => string '..\lab\stockoverflow\c.php' (length=46)
  3 => string '..\lab\stockoverflow\d.php' (length=46)

But you can do something like

$inc = new IncludeManager($file);
var_dump($inc->find("b.php")); // Check if a file is included
var_dump($inc->getFiles("require_once")); // Get All  Required Once 

Class Used

class IncludeManager {
    private $list = array();
    private $tokens = array();
    private $find;
    private $file;
    private $type = array(262 => "include",261 => "include_once",259 => "reguire",258 => "require_once");

    function __construct($file) {
        $this->file = $file;
        $this->_parse();
    }

    private function _parse() {
        $tokens = token_get_all(file_get_contents($this->file));
        for($i = 0; $i < count($tokens); $i ++) {
            if (count($tokens[$i]) == 3) {
                if (array_key_exists($tokens[$i][0], $this->type)) {
                    $f = $tokens[$i + 1][0] == 371 ? $tokens[$i + 2][1] : $tokens[$i + 1][1];
                    $this->list[] = array("pos" => $i,"type" => $this->type[$tokens[$i][0]],"file" => trim($f, "\"\'"));
                }
            }
        }
    }

    public function find($find) {
        $finds = array_filter($this->list, function ($v) use($find) {
            return $v['file'] == $find;
        });

        return empty($finds) ? false : $finds;
    }

    public function getList() {
        return $this->list;
    }

    public function getFiles($type = null) {
        $finds = array_filter($this->list, function ($v) use($type) {
            return is_null($type) ? true : $type == $v['type'];
        });
        return empty($finds) ? false : $finds;
    }
}
Baba
  • 94,024
  • 28
  • 166
  • 217
2

Here's a different idea. Just include the file whenever you need it. Inside the include file you can decide whether it needs to include the contents:

<?php
if (defined("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE"))
    return;
define("SOME_UNIQUE_IDENTIFIER_FOR_THIS_FILE", 1);

// Rest of code goes here
muz the axe
  • 418
  • 2
  • 17
  • This reminds me of the [include guard definition](https://en.wikipedia.org/wiki/Include_guard) commonly used in C header files. – Magnus Dec 26 '20 at 08:20
2

Working solution:

$target_file = '/home/path/folder/file.php'; // or use __FILE__

if ($x=function($e){return str_replace(array('\\'), '/', $e);}) if(in_array( $x($target_file), array_map( $x ,  get_included_files() ) ) )
{
    exit("Hello, already included !");
}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
0

I don't think get_included_files is the perfect solution, what if your main script included some other scripts before the check? My suggestion is to check whether __FILE__ equals realpath($argv[1]):

<?php
require('phpunit/Autoload.php');

class MyTests extends PHPUnit_Framework_TestCase
{
    // blabla...
}

if (__FILE__ == realpath($argv[0])) {
    // run tests.
}
adamsmith
  • 5,759
  • 4
  • 27
  • 39
0

I took a similar approach to this issue when I cam across it. The solution I found was to load each file as needed in an include_once method. Hope this helps.

$FILES = get_included_files();  // Retrieves files included as array($FILE)
$FILE = __FILE__;               // Set value of current file with absolute path
if(!in_array($FILE, $FILES)){   // Checks if file $FILE is in $FILES
  include_once "PATH_TO_FILE";  // Includes file with include_once if $FILE is not found.
}

I have the following function established to check files loaded:

ARRAY_DUMP($FILES);

function ARRAY_DUMP($array){
  echo "
    <span style='font-size:12px;'>".date('h:i:s').":</span>
    <pre style='font-size:12px;'>", print_r($array, 1), "</pre>
  ";
}

Output:

currentArray
(
  [0] => /home/MY_DOMAIN/hardeen/index.php
  [1] => /home/MY_DOMAIN/hardeen/core/construct.php
  [2] => /home/MY_DOMAIN/hardeen/core/template.php
  [3] => /home/MY_DOMAIN/hardeen/bin/tags.php
  [4] => /home/MY_DOMAIN/hardeen/bin/systemFunction.php
)