PHP include路径是相对于文件还是调用代码?

124

我在理解PHP相对包含路径规则集方面遇到了麻烦。如果我运行文件A.PHP,并且文件A.PHP包含文件B.PHP,而文件B.PHP又包含文件C.PHP,那么C.PHP的相对路径是基于B.PHP的位置还是基于A.PHP的位置?也就是说,包含调用的文件是否重要,或者只有当前工作目录重要,而是什么决定了当前工作目录?


在我看来,比被接受的答案更准确:https://dev59.com/zWs05IYBdhLWcg3wNPO7#23902890。 - user1636522
6个回答

141

这取决于主脚本,即此处的 A.php。请记住,include() 仅将代码插入当前运行的脚本中。

也就是说,调用 include 的文件是否有关系?

没有。

如果你想要让它有关系并且相对于 B.php 进行包含,请使用 __FILE__ 常量(自 PHP 5.2 以来还可以使用 __DIR__),它将始终指向包含该行代码的字面上的当前文件。

include(dirname(__FILE__)."/C.PHP");

8
你也可以使用__DIR__来实现完全相同的目的。 - Nick Bedford
1
这个答案有点过度:不需要 include(dirname(__FILE__)."/C.PHP");,因为 include("C.PHP"); 就足够了(是的!C.PHP可以和B.PHP在同一个目录下)。只有当项目中有两个C.PHP文件时才可能失败。 - Johnny Wong
它可能会以许多方式失败。如果未出现在其中,PHP的include_path可能会配置错误,因此它不会查找当前工作目录。此外,工作目录可以通过入口点或使用chdir更改(如您的帖子所示)而更改。在通过相对包含项时,它需要浏览完整的include_paths列表,并且根据配置可能会找到“错误”的文件。使用绝对路径(例如__DIR__dirname(__FILE__)等)要少出错得多。但是,如果您知道风险,请随意使用相对路径。 - Kevin Y
请注意,dirname函数有一个可选的第二个参数(整数类型),它使您能够相对于当前脚本遍历“n”个父目录。您可以在此处找到更多详细信息:https://www.php.net/manual/en/function.dirname.php - G M
@KevinY 谢谢,但请注意,即使 include_path 中没有 .. 也会被搜索到。在 PHP 文档中有这样的说明:“如果在 include_path 中找不到文件,include 最终会在调用脚本的目录和当前工作目录中进行搜索,直到失败为止。” - undefined
显示剩余2条评论

26

@Pekka 指出了我的问题,但是我想分享一下我学到的内容:

getcwd() 返回执行脚本所在的目录。

dirname(__FILE__) 返回包含当前正在执行代码的文件的目录。

使用这两个函数,您可以始终构建与您需要的路径相关的包含路径。

例如,如果 b.php 和 c.php 共享一个目录,b.php 可以像下面这样包含 c.php:

include(dirname(__FILE__).'/c.php');

无论b.php从哪里调用,都可以使用这种方式。实际上,这是建立相对路径的首选方式,因为额外的代码使PHP无需尝试遍历include_path以定位目标文件。

来源:

getcwd()和dirname(__FILE__)之间的区别?我应该使用哪个?

为什么你应该使用dirname(__FILE__)


我想补充一些内容,我有一个名为csb.php的文件,从一个文件夹中包含了一个函数文件,而函数文件又从与其相同的文件夹中包含了一个名为t.php的文件, 当我尝试从t.php所在的文件夹中包含一个名为csb.php的文件时,它开始包含调用函数文件的相同csb.php,但当我将第二个csb.php更改为csbe.php时,它立即开始正常工作。因此,它似乎优先考虑第一个文件夹,然后是第二个包含文件夹! - Miguel Vieira

19
如果包含路径不以./../开头,例如:
 include 'C.php'; // 优先级:include_path(首先包含'.'),
                  // 然后是当前`.php`文件的路径(即`B.php`),然后是`.`。

如果包含路径以./../开头,例如:
 include './C.php';  // 相对于'.'

 include '../C.php'; // 也相对于'.'

上面的 . .. 是相对于 getcwd() 的,它默认为入口 .php 文件的路径(即 A.php )对于某些Web服务器,如Apache。

在PHP 5.4.3上进行了测试(构建日期:2012年5月8日00:47:34)。

(还要注意 chdir() 可以更改 getcwd() 的输出。)


总之,首先搜索 A.php 所在的路径以查找 C.php,如果 include 中没有前缀 "./" 或 "../",则会搜索 B.php 的路径。(假设使用默认的 PHP 设置) - Johnny Wong
谢谢你指出使用 chdir(__DIR__) 来解决问题。 - HartleySan
[...] getcwd(),默认为入口.php文件的路径 [...] - 不一定是真的。如果我在命令行上运行PHP,则getcwd()指的是shell的当前工作目录,而不管我调用哪个.php文件。不过我可以想象,如果PHP在Web服务器环境中运行,那么该环境会将当前工作目录初始化为入口.php文件。在macOS上使用通过Homebrew安装的PHP 7.2.2进行测试。 - herzbube
@herzbube 谢谢。我改进了那部分,使其更适用于常见的网络服务器情况。 - undefined

19
Pekka的被接受的答案是不完整的,并且在一般情况下会误导。如果文件作为相对路径提供,调用的语言结构include将按以下方式搜索它。首先,它将通过环境变量include_path的路径进行搜索,该变量可以使用ini_set设置。如果失败,则会在调用脚本自己的目录dirname(__FILE__)(php >= 5.3时为__DIR__)中搜索。如果这也失败了,那么它才会在工作目录中搜索!事实证明,默认情况下,环境变量include_path . 开头,即当前工作目录。这就是为什么它首先在当前工作目录中搜索的唯一原因。请参见http://php.net/manual/en/function.include.php

基于给定的文件路径或指定的include_path,包含文件。如果在include_path中找不到文件,则在调用脚本自己的目录和当前工作目录中进行最后检查,然后失败。

所以,第一个问题的正确答案是包含调用脚本的位置很重要。对于最后一个问题,初始工作目录在Web服务器上下文中是被PHP处理时包含所有其他脚本的脚本所在的目录。在命令行上下文中,初始工作目录是在提示符处调用php时的任何目录,不一定是调用脚本所在的目录。然而,当前工作目录可以使用PHP函数chdir在运行时更改。请参见http://php.net/manual/en/function.chdir.php
这段文字是用来评论其他答案的。有些人提到依赖于include_path不够健壮,因此最好使用完整路径,例如./path__DIR__ . /path。有些人甚至说依赖于工作目录.本身并不安全,因为它可以被更改。然而,有时候你需要依赖环境值。例如,你可能想将include_path设置为空,这样调用脚本的目录就是它首先搜索的地方,甚至在当前工作目录之前。代码可能已经编写并定期从外部源更新,你不想每次更新代码时都重新插入前缀__DIR__

如果失败了,它将在调用脚本的目录 dirname(__FILE__) (__DIR__) 中搜索(要求 PHP 版本 >= 5.3)。你确定吗?这在哪里有记录?我希望你是错的,PHP 会为了这个目的使用 __FILE____DIR__,因为这会立即破坏从符号链接中包含“同级”脚本! :-o (幸运的是,在我的 7.1 设置中,这似乎可以正常工作。) - Sz.

6
简短回答:它是相对于包含脚本而言的。 TFM 解释得很正确:
如果在include_path中找不到文件,则include会检查调用脚本目录和当前工作目录。
因此,如果 /app/main.php 中包含include("./inc.php"),那么将找到 /app/inc.php./ 不是必需的,但可以消除对include_path的依赖。
我不会指望在当前工作目录中找到包含文件,以防万一有人使用chdir()更改它。

所以,如果您使用./开头的字符串,它会首先检查调用脚本的目录还是当前工作目录? - Pacerier
2
@Denis Howe 不,如果您的包含路径以 ./ 开头,则已经依赖于当前工作目录。例如,chdir("/app/other") 将使 include("./inc.php") 失败。因此,在这种情况下,请使用 include("inc.php") 以确保安全。 - Johnny Wong
@Pacerier 如果字符串以 ./ 或 ../ 开头,则仅检查当前工作目录。(include_path 和调用脚本(B.php)的目录都将被忽略)。有关更多详细信息,请参见我的答案。 - Johnny Wong

-2
dir
-> a.php
-> c.php

- dir2 
-> b.php

如果要在b中包含a,则需要使用include("../a.php");

如果要在c中包含b,则需要使用include("dir2/b.php");


5
@Olli-你错过了问题的要点-我正在问关于如何确定相对路径的,当包含被链接时。 - Yarin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接