问题 PHP包含所需的文件策略


我正在设置一个php项目,但我不太熟悉如何正确使用php的include / require命令。我的布局目前看起来像这样:

/public   --apache points into this directory  
/public/index.php  
/public/blah/page.php  
/utils/util1.php       -- useful classes/code are stored in other directories out here  
/dbaccess/db1.php  

dbaccess/db1.php  



require '../utils/util1.php

公共/ index.php文件

require '../dbaccess/db1.php'

公共/胡说/ page.php文件

require '../../dbaccess/db1.php'

问题来自php'include'文档:

如果filename以./或../开头,则仅在当前工作目录中查找

所以public / blah / page.php失败了,因为它包含dbaccess / db1.php,它在尝试包含util1.php时会爆炸。它失败了,因为它的相对路径来自public / blah /中的原始脚本,而不是来自dbaccess /

这看起来很愚蠢 - db1.php必须知道它被包含在哪里不起作用。

我见过这样的策略:

require_once dirname(__FILE__) . '/../utils/util1.php');

这显然是有效的,因为现在路径是一条绝对的道路,但对我来说似乎真的很奇怪。

这是正常的吗?我应该继续沿着这条路走下去,还是我错过了一些明显的东西?


1923
2017-12-04 00:07


起源

我没有太多补充,除了说我已经使用PHP 5年了,这件事 仍然 困惑我。 - nickf


答案:


通常,标准约定是这样的:像@grepsedawk所说,你需要定义一个包含项目文件夹根目录的常量,如果你可以包含你的包文件夹的根目录:

define('APP_ROOT', dirname(__FILE__));
define('INCLUDE_ROOT', APP_ROOT . "/includes");

注意:常量名称必须是一个字符串!

另外,你会注意到我正在使用 dirname(__FILE__);。如果将常量定义文件放在子目录中,则可以执行 dirname(dirname(__FILE__));,相当于一个 ../

现在还有其他一些警告。而 PATH_SEPARATOR 是一个很酷的常数,它是不需要的。 Windows在路径名中接受/或\,并且因为Linux只有用户/作为路径分隔符,所以继续使用/而不是通过重复引用来修改代码 PATH_SEPARATOR

现在您已经定义了根常量,当您需要包含配置文件时,您将要执行的操作很简单:

include INCLUDE_ROOT . '/path/to/some/file.php';

你可能想要你的常量定义( define(...)在上面的根目录中的bootstrap脚本中:

www_root/
  index.php
  bootstrap.php

引导程序将包含定义(或 include 常量文件),以及 include 每页所需的任何文件。

最后是你可能不会使用的最后一个标准约定,但是如果你开始进行面向对象的编程,最常见的方法(PEAR标准)是通过使用_来分隔命名空间来命名你的类:

class GlobalNamespace_Namespace_Class
//...

然后组织你的文件结构映射名称空间到子目录(字面上用/'替换所有_):

include_dir/
  GlobalNamespace/
      Namespace/
          Class.php

并使用 __autoload() 加载类的函数,但这是另一个问题。


13
2017-12-04 16:36



我忘了提,用dirname(文件)让您可以自由地使用完整路径(无需担心./或../解析),而无需在部署服务器之间移动时维护完整路径(例如“oops复制/ home / httpd / ... /生活应该是/var/www/.../“) - dcousineau
你的意思是DIRECTORY_SEPARATOR,而不是PATH_SEPARATOR! PATH_SEPARATOR用于include_path,在Windows上是分号,否则是冒号。阅读更多 这里 - Alexxus


有一个配置脚本,用于设置项目的“INSTALL ROOT”,然后使用绝对路径。具有多个包含的相对路径是php中的头疼。

DEFINE(“INSTALL_ROOT”,“/ path / to / www / project”)

require_once(INSTALL_ROOT。'/ util1.php')


4
2017-12-04 00:18



我在哪里设定全局定义?我显然不想在每个文件中都这样做。 - Clyde
克莱德:在配置脚本中,就像文本建议的那样。 - OIS
必须使用字符串作为名称来定义常量。如果关闭错误报告,该代码看起来会有效,但仅仅因为如果PHP找不到与该字符串文字匹配的常量,它只会将其转换为字符串。 define('INSTALL_ROOT', '/path/to/dir/'); 是正确的。 - dcousineau
更新了dcousineaus评论。这并不是真正意义上的代码:) - grepsedawk
最好不要混淆:P - dcousineau


在我的配置/设置文件中,我做了类似的事情

define('MYAPP_BASEDIR',realpath('.'));

然后我引用与此相关的所有内容。

...如果您的include目录专门与类文件相关,并且您可以命名它们以便可以从类派生包含文件名,那么您可能希望查看 spl_autoload_register()

后一部分不是你问题的直接答案,但如果你为你使用的每个课程做了包括,那么它就非常方便。


1
2017-12-04 02:29





请记住,它从当前工作目录开始,然后查看包含路径。如果你想从一些中心根目录(或许多)引用你的所有路径,你可以在php.ini文件中添加该目录,或者你可以用程序编写 set_include_path( $path.PATH_SEPERATOR.get_include_path());


0
2017-12-04 01:52





我建议采用抽象策略。

  1. 在您的应用程序页面区域中,有一个所有页面都包含的文件。

  2. 这个“本地”包含文件有一个作业:找到应用程序页面区域之外的包含文件。然后包括那个。它可能很简单 <?php include dirname(__FILE__).'/../include/include.php/'; ?>

  3. 这第二个文件是 单一入口点 进入你的图书馆结构。它或它包含的其他东西,其工作就是找到所有东西,包括它。

这种结构意味着您只有一个文件作为库的入口点,以及它如何找到库的其余部分不是应用程序页面的问题。这也意味着您的应用程序区域中只有一个文件,它知道如何查找库的入口点。

如果您需要一种方法让不同的应用程序页面加载不同的东西,我建议采用模块化方法。这可以是您在master包含之前设置的全局数组,也可以是您可以调用以按名称请求库的函数。是的,这是一个稍微有点方式的主库文件,声明一切都在哪里 - 但它消除了做的诱惑 include LIBRARY_DIR.'/utils/util.php'; 直接使它不必要地移动 util.php 在......之外 utils 进入 misc/util 在以后的日子。

链接文件的另一个优点是,使代码库可重定位更容易,这使得多个版本可以运行。并且它可以为应用程序创建一个树,为库创建另一个树。这表示另一个应用程序可以使用您的库。实际上,如果您想进一步帮助隔离,可以将链接扩展一些。


0
2017-12-04 02:10





你是对的,你的脚本不必知道你的包含的物理路径。

IMO应该在PHP.INI文件中配置包含的位置(如果你预先配置,则为.htaccess)。

Suponse你的包含(utils和数据库存储在这里/ home / scott / php_includes /)。

PHP.INI:

include_path中=:/家庭/斯科特/ php_includes /

现在,您的脚本可以通过以下方式包含库:

DBACCESS / db1.php:

require_once'utils / util1.php';

公共/ index.php文件

require_once'dbaccess / db1.php';

公共/胡说/ page.php文件:

require_once'dbaccess / db1.php';


0
2017-12-04 19:22





很多人提供了很好的解决方案,但在谈论包含和要求时,我只得到了一个与性能相关的评论。

如果你开始包含'并且需要'很多文件,那么使用include_once或require_once可能很诱人。使用大量_once的高速缓存的脚本表明它们确实会降低性能,因为脚本必须停止其扫描操作,并确保文件尚未包含在内。消除尽可能多的_once可以帮助很多。


0
2017-12-04 19:29





有完美的解决方案 - pecl扩展称为“pwee” - 它允许用户使用XML文件定义他/她自己的外部超全局常量/变量。因此,您可以使用绝对路径,因为我建议以这种形式:

require_once APP_ROOT."/path/to/your/script.php";

这种解决方案的优点是:

  • 从各处都可以访问
  • 无服务器负载 - 服务器内存中的所有内容

包含的XML文件

  <Environments>
    <Application name="www.domain.com" namespace="">
      <Constants>
        <Constant name="APP_ROOT" value="/full/path/to/project/source" />
      </Constants>
      <Constants>
        <Constant name="WEB_ROOT" value="/full/path/to/project/public" />
      </Constants>
    </Application>
  </Environments>

链接到pwee项目

您应该区分这些包含的情况:

  • 独立库 - 所有包含都应该是相对的 - 让用户轻松地将它集成到他/她的项目中
  • 公共目录中的可执行脚本 - 包含项目文件和独立库公共文件的绝对包含(其中包含相对内部 - 对用户透明)。使用APP_ROOT常量是优雅的方式。
  • a,链接,脚本,表单html元素和标题转发应该在潜入树层次结构时使用相对路径和使用更高层次结构的公共文件时的绝对路径

如果是相对路径,请使用此表单:

  require_once "./relative/path/to/script.php";

为什么我使用过去时态?因为项目不再受支持 - 仅适用于Php4。如果有人知道类似的解决方案,请告诉我。


0
2017-12-19 13:44





最好的方法是构建灵活的自动加载系统。

简单的类名和专有补丁映射。然后不需要任何内部require_ *或include_ *。

当然有自动加载器的相对/绝对路径的问题。嗯,绝对是系统效率最高的,所以在我之前提到的数组中你可以预先添加某种变量{我使用过Phing-style变量}例如

<map>
    <path classname="MyClass">${project_directory}/libs/my_classes/MyClass.php</path>
    <path classname="OtherClass">${project_directory}/libs/some_new/Other.php</path>
    <!-- its so flexible that even external libraries fit in -->
    <path classname="Propel">${project_directory}/vendors/propel/Propel.php</path>
    <!-- etc -->
</map>

这是xml(想想ini或yaml)文件,并且需要在第一次启动时编译到php,但之后任何路径都是绝对的。

哦,你可以看到没有文件命名约定或文件布局是强制性的 - 它的巨大优势。

干杯,艾伦。


0
2017-12-27 23:24