设置包含/引用文件的路径

5

我的所有PHP包含文件都在一个单独的目录中:

https://www.mywebsite.com/includes

在顶层页面中插入这些文件非常容易:
<?php include 'includes/logo.php'; ?>
<?php include 'includes/main_nav.php'; ?>
<?php include 'includes/news.php'; ?>
etc.

对于子目录页面,我一直这样做:

<?php include '../includes/logo.php'; ?>
<?php include '../includes/main_nav.php'; ?>
<?php include '../includes/news.php'; ?>

和这个:

<?php include '../../includes/logo.php'; ?>
<?php include '../../includes/main_nav.php'; ?>
<?php include '../../includes/news.php'; ?>

到目前为止一切顺利,但我怀疑这并不会一直这么容易。
现在我需要包含这个文件:
top_five_news_stories.php

在这个中:
news.php

此时,我的相对路径策略失败了,因为包含在包含中的路径结构只能有一个。
我阅读了几篇帖子,推荐使用绝对路径:
- dirname(__FILE__) - realpath(dirname(__FILE__)) - $_SERVER["DOCUMENT_ROOT"]
但是,它们都带有某种与PHP配置、服务器配置或操作系统相关的警告。换句话说,通常会有人评论说在他们的情况下不起作用,或者在IIS中不起作用,或者在UNIX中不起作用,或者其他原因。
我没有看到的解决方案是我认为最简单的:只需设置一个变量。
$base = "https://www.mywebsite.com/includes";

然后:
<?php include $base . "logo.php" ?>

考虑到我已经使用了与之类似的HTML base元素,这种方法让我感觉简单高效。

但由于在我阅读的任何帖子中都没有提到它,所以我想知道是否忽略了潜在的问题。

坦白地说,如果我今天必须开始生产,我会使用这个:

<?php include  $_SERVER['DOCUMENT_ROOT'] . '/logo.php" ?>

这对我很有用,而且常常被提到。

但我想知道使用变量是否是一种可靠、高效的方法?


1
不要在 include 中使用 URL。当您通过 Web 服务器访问 PHP 脚本时,它会在远程端执行它,而不是返回源代码,但 include 需要源代码。 - Barmar
请查看 php.ini 中的 include_path 设置。将您的包含文件放在该目录中,然后您就可以从任何脚本中访问它们了。 - Barmar
http://php.net/manual/en/ini.core.php#ini.include-path - Barmar
4个回答

13

不要

我建议不要使用需要PHP之外的东西的任何东西,比如$_SERVER变量。

$_SERVER ['DOCUMENT_ROOT'] 通常由Web服务器设置,这使其无法用于从命令行运行的脚本。所以不要使用此方法。

同时不要使用URL。 URL中的路径部分与磁盘上文件的路径不同。实际上,该路径甚至可能不存在于磁盘上(考虑Apache重写)。

包含URL还需要您打开allow_url_include,如果使用不当,会引入(严重的)安全风险。

如果您支持的最低PHP版本是5.3(希望如此!),则可以使用魔术常量 __DIR__,以下是2个示例:

define(ROOT_DIR, __DIR__);
define(ROOT_DIR, realpath(__DIR__ . '/..'));

如果您需要支持较低版本,请使用dirname(__FILE__)。以下是两个示例:

define(ROOT_DIR, dirname(__FILE__));
define(ROOT_DIR, realpath(dirname(__FILE__) . '/..'));

确保ROOT_DIR指向你的项目根目录,而不是其中某个子目录。

接着你可以安全地使用ROOT_DIR来包含其他文件:

include ROOT_DIR . '/some/other/file.php';

请注意,我正在定义一个常量(ROOT_DIR),而不是变量。变量可以改变,但项目的根目录不会改变,因此常量更适合。

realpath()

realpath()将解析任何相对部分和符号链接到规范化的绝对路径名。

所以给出以下文件和符号链接:

/path/to/some/file.php
/path/to/another/file.php
/path/to/symlink => /path/to/another

并且/path/to/file.php包含:

define(ROOT_DIR, realpath(__DIR__ . '/../symlink'));

如果你依赖于相对路径或符号链接,那么不使用 realpath() 也可以,但它可以整理路径并使调试更容易。如果您需要包含包含类的文件,最好使用自动加载,这样就不需要include语句了。最后一条建议是:这个问题已经被解决了很多次。我建议您看看像Symfony、Zend Framework、Laravel等框架。如果您不想要一个“全栈”解决方案,请查看Silex、Slim、Lumen等微框架。

非常好的答案。它帮助我更好地理解需要做什么。谢谢。 - Michael Benjamin
关于您提到的框架建议,我正在从头开始重新设计一个现有的企业级网站。在适当的情况下,我会使用我知道的框架。您在回答末尾提到的框架对我来说是新的。我会研究一下它们。 - Michael Benjamin

2
Jasper提出了一些好的观点,不使用DOCUMENT_ROOT的另一个原因是可通过URL访问的内容不一定在此目录中(例如考虑Apache的别名、脚本别名和mod_user_dir)。
正如Barmar所指出的,PHP明确提供了用于声明包含文件基目录的功能。尽管通常在配置中设置,但您可以在代码中运行时覆盖/添加。您永远不应在包含/需要指令中看到变量。它会破坏自动工具并隐藏漏洞。您也不应该使用文件包装器进行包含。
在面向对象编程中,有一个论点认为永远不要显式使用包含/需要,而只需自动加载类定义。但是,定位代码的问题仍然存在。
简短的答案是,对于您描述的问题,没有最佳解决方案。每种方法都有其缺点-最佳解决方案完全取决于上下文。对于企业应用程序,设置include_path简化了开发过程,并且如果不能直接从Web服务器访问,则增强了安全性。它还允许通过操纵路径中多个条目的顺序来有选择地覆盖功能。
另一方面,这不是要分发给技术水平较低的用户并可能对多个路径感到困惑的软件的好模型,他们可能无法访问文档根目录之外的目录或更改默认配置。
使用相对路径是一种强大且可移植的解决方案。我不明白您在包含top_five_news_stories.php方面的问题。
下面是一种既具有企业又具有低端托管优势的解决方案。但是,这具有缺点,即需要向站点中的每个入口添加代码(并且需要将应用程序安装在命名子目录中):
define('APP_NAME', 'mikesdemo');
$base=substr(__DIR__, 0, strrpos(__DIR__, APP_NAME))
      . APP_NAME . '/include';
set_include_path(get_include_path() . PATH_SEPARATOR . $base);

更有经验的用户可以简单地使用命令行界面来控制系统,这样可以更高效地完成任务。
mv /var/www/html/mikesdemo/include/* /usr/local/php/include/

感谢您提供这么详细的答案。如果我在关于“top_five_news_stories.php”方面没有表达清楚,我深表歉意。 - Michael Benjamin

1

文件架构重构:

为每种类型的文件定义一个路径,如下所示:

+root
|
+------app(D)(all php script MVC)
|
+------conf(D)(all php Config file)
|
+------assets(D)(all file js and css static image)
|
+------fileup(D)(all file Uploades)
|
+------index.php(F)(Procesor of petition http)

在您的索引中,您需要包含所有配置文件,例如C++样式:

示例:

require_once ('conf/config.security.php'); #Configuration around Security in PHP
require_once ('conf/config.conpro.php'); #Configuration around Constantent
require_once ('conf/config.classlib.php'); #Lib class around Generic DB Conection ETC
require_once ('conf/config.classlibmvc.php'); #Lib class around MVC specific class

这是一个配置文件的示例:

声明根目录和共享文件的路径

$APP_DIR_CLASS          =       $_SERVER['DOCUMENT_ROOT']   .   '/app/classgeneric/';

定义库文件:

if (!defined('DBMANAGER_CLASS'))                define('DBMANAGER_CLASS'            ,'class.managerdb.php'          );

包含或者引用类

require_once $APP_DIR_CLASS     . DBMANAGER_CLASS;

当您在一个类中需要使用DB类时,可以轻松调用它:
class Class_Exmaple{
    public function __construct(){
        $this   ->  DBMANAGER               =   new Class_BDManager();
    }
    public function __destruct(){
        $this   ->  DBMANAGER               =   new Class_BDManager();
    }
    public function ConsultDB(){
        $query ='Selec * From Tablename';
        $result = $this -> DBMANAGER -> ExecuteQ($query);
        print_r(result);
    }
}

这是一种简单的实现方式,但是您需要了解有关注入和类加载器的更多信息。


谢谢您的回答。就一般知识而言,它很有用,但我在将其应用到我的具体情况时遇到了困难。如果您有任何其他建议,请告知。这是我上面问题的快速摘要:https://jsfiddle.net/2npxxLwa/1/ - Michael Benjamin
你能分享一下 include 文件的内容吗? - user6079755
这只是文本。正在构建框架。目前还没有实际内容。 - Michael Benjamin

1

在项目中,没有一种“正确的方式”来要求/包含内部脚本。许多(大多数)MVC框架使用类似的“最佳实践”来访问路由器对象中的全局文件。

让我们举个例子,这是我们的目录结构:

App/
    Controllers/
        Controller.php
    Models/
        Model.php
    Views/
        View.php
    404/
        index.php
index.php
.htaccess

在我们的.htaccess文件中,我们将有一个rewrite规则到您服务器根目录下的index.php
在这个文件中,我们实际上运行了整个软件。例如,这是我使用的一个很好的路由器AltoRouter
首先,我们需要添加一种方法来阻止直接浏览器访问,并为任何控制器、模型和视图添加错误路径:
define( 'ERROR_PATH', strtolower(explode( '/', $_SERVER['SERVER_PROTOCOL'][0]) . '://' . $_SERVER['SERVER_NAME'] . '/404' );
define( 'IN_APP', 0 );

然后在您的控制器、模型和视图中使用,例如:

if( !defined( 'IN_APP' ) )    {
    header('Location: ' . ERROR_PATH);
    exit();
}

如果在此实例(index.php)中声明__FILE__,则您的文件路径将是文件路径,因此我们可以以任何方式使用它(最佳实践是全局定义)。请保留HTML标签。
 define( '_DIR_', dirname(  __FILE__ ) );

然后开始要求您的文件:

$includeFiles = [
    glob( _DIR_ . '/Controllers/*.php' ),
    glob( _DIR_ . '/Models/*.php' )
];

foreach( $includeFiles as $dir ):
    foreach( $dir as $file ):
        require_once( $file );
    endforeach;
endforeach;

感谢您的回答。我很感激您提到如何防止直接浏览器访问include/require文件,因为这是我一直在思考但尚未解决的问题。我仍在阅读这里的答案,以开发出正确的解决方案。如果您想添加更多内容,请参考以下问题摘要:https://jsfiddle.net/2npxxLwa/ - Michael Benjamin
看起来防止浏览器直接访问包含/需要文件的目录的最简单方法是在该目录中放置一个带有“deny from all”的“.htaccess”文件。到目前为止,在我所有的测试中都有效。https://dev59.com/MXRC5IYBdhLWcg3wG9Nb#409511 - Michael Benjamin
是的,但使用.htaccess并不可靠,因为这也会停止对图像的访问。最好将所有不存在的请求都专用于索引文件,然后将请求路由到那里。然后只允许使用define()defined()方法访问特定文件。这是许多开源Web应用程序(如MyBB)中使用的基本最佳实践。@Michael_B - Jaquarh

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