从 include() 获取调用文件名


Get calling file name from include()

我想从包含的文件内部获取包含另一个文件的文件的名称。

我知道有__FILE__魔术常量,但这无济于事,因为它返回包含文件的名称,而不是包含文件的名称。

有什么办法可以做到这一点吗?还是由于PHP的解释方式而不可能?

所以这个问题很老了,但我正在寻找答案,在不满意的情况下离开这里后,我遇到了$_SERVER['SCRIPT_FILENAME']; 当然,如果执行包含的 php 文件是一个网页,这有效。

这为您提供了服务器上"包含文件"的完整路径。 例如/var/www/index.php.所以如果你只想要文件名,例如 index.php,你将需要使用 basename() eg

basename($_SERVER['SCRIPT_FILENAME']);

因此,如果在索引中.php则有以下行:

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

在某些php中.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 ,这也有效。

解决方案

知道用于包含文件的函数是 includeinclude_oncerequirerequire_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();
?>

文件索引.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');
?>

文件索引.php

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

在上面的例子中,文件索引.php将包括 x.php,然后 x.php 将包括索引.php返回,然后是索引.php输出 "something" 并将控制权返回给 x.php,x.php 将控制权返回给索引.php然后到达 exit;

这表明,即使 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);                                                                                                                                                                     
    } 

愿这对你有帮助:)

这实际上只是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;

这行得通!

将其包含在正在调用的文件中。

$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.';
}