Получить имя вызывающего файла из include()

Я хочу получить имя файла, который включает в себя другой файл из включенного файла.

Я знаю, что есть __FILE__ магическая константа, но это не помогает, так как она возвращает имя включенного файла, а не включающего.

Есть какой-либо способ сделать это? Или это невозможно из-за того, как интерпретируется PHP?

5 ответов

Решение

На самом деле это всего лишь особый случай того, что делают движки шаблонов PHP. Рассмотрите возможность использования этой функции:

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

затем A.php может включать C.php как это:

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

Дополнительно, B.php может включать C.php так же без проблем.

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

C.php может узнать его включение, посмотрев в массив $params.

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

Так что этот вопрос довольно старый, но я искал ответ и, оставив неудовлетворенным, наткнулся на $_SERVER['SCRIPT_FILENAME']; Конечно, это работает, если php-файл, выполняющий включение, является веб-страницей.

Это дает вам полный путь к "включающему файлу" на сервере. например /var/www/index.php. так что если вам нужно только имя файла, например index.php, вам нужно будет использовать basename(), например

basename($_SERVER['SCRIPT_FILENAME']);

Итак, если в вашем index.php у вас есть следующая строка:

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

и в somephp.php у вас есть

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

ты получишь

this is the file that included me: index.php

вывод в браузер. Это также работает, если пользователь обращается к вашему файлу без явного включения имени файла в URL, например www.example.com вместо www.example.com/index.php,

Решение

Зная, что функции, используемые для включения файлов include, include_once, require а также require_once также зная, что они являются зарезервированными словами в PHP, это означает, что невозможно объявить пользовательские функции с одинаковыми именами, и основано на идее использования Wedgwood debug_backtrace вы действительно можете определить, из какого файла было вызвано включение.

Что мы собираемся сделать, так это перебирать обратную трассировку до тех пор, пока мы не найдем самый последний вызов любой из четырех включаемых функций и файл, в котором она была вызвана. Следующий код демонстрирует эту технику:

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;
}

Приведенный выше код вернет абсолютный путь к файлу, в котором произошло последнее включение, если его не было, он вернет false. Обратите внимание, что файл может быть включен из файла, который был включен из другого файла, вышеупомянутая функция работает только для самого глубокого включения.

С помощью простой модификации вы также можете получить последний включенный файл:

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;
}

наблюдения

Обратите внимание, что __FILE__ это не включенный файл, а текущий файл. Например:

файл A.php:

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

файл B.php:

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

файл index.php:

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

В приведенном выше примере в контексте файла B.php включенным файлом является B.php (а включающим файлом является index.php), но вывод функции callme это путь A.php, потому что __FILE__ находится в A.php.


Также обратите внимание, что $_SERVER['SCRIPT_FILENAME'] даст абсолютный путь к скрипту, запрошенному клиентом. Если $_SERVER['SCRIPT_FILENAME'] == __FILE__ это означает, что текущий файл является запрошенным, и, следовательно, там, вероятно, не было никаких включений...

Вышеупомянутый метод проверяет, является ли текущий файл запрошенным, но не, если он не был включен (ниже пример того, как запрошенный файл может быть включен). Фактическое решение, чтобы проверить, не было ли каких-либо включений, может быть, чтобы проверить count(get_included_files()) == 1,


Запрашиваемый файл может быть включенным файлом следующим образом:

файл x.php

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

файл index.php

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

В приведенном выше примере файл index.php будет включать в себя x.php, затем x.php будет включать index.php назад, после чего index.php выводит "что-то" и возвращает управление в x.php, x.php возвращает управление в индекс.php и доходит до выхода;

Это показывает, что даже если index.php был запрошенным скриптом, он также был включен из другого скрипта.

Я не могу найти легкий способ покрыть это. Но если включающий действительно важен для вас, вы можете взломать его с помощью некоторой глобальной переменной и собственной функции включения.

например

<?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);                                                                                                                                                                     
    } 

Пусть это будет полезно для вас:)

Это работает!

Включите это в вызываемый файл.

      $includedFiles = get_included_files();
$currentFile = realpath(__FILE__);

$callerFile = '';
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

foreach ($backtrace as $trace) {
    if (isset($trace['file']) && realpath($trace['file']) !== $currentFile) {
        $callerFile = $trace['file'];
        break;
    }
}

if (!empty($callerFile)) {
    echo 'Included by: ' . $callerFile. '</br>';
} else {
    echo 'No direct inclusion found.';
}

Другие вопросы по тегам