获取基础url,开发变量,最好的方式,不使用全局变量



在我的应用程序架构中,我想用一些不会伤到大多数开发人员眼睛的东西来替换全局变量,因为我使用全局变量像这样,

define('DEVELOPMENT_ENVIRONMENT', true);
// Shorten DIRECTORY_SEPARATOR global,
define('DS', DIRECTORY_SEPARATOR);
// Set full path to the document root
define('ROOT', realpath(dirname(__FILE__)) . DS);

我该如何预防?我尝试创建一个类,读取xml文件,但这会给我一个更长的代码,像这样

$c = new Config();
if($c->devmode === TRUE) {}

或者像这样

$c = new Config()
echo $c->baseurl;

有更好的方法吗?

我认为像你这样的问题一般是无法回答的,但它们可能无论如何都值得一个答案。只是没有一个黄金法则或解决方案来处理这个问题。

在最简单的意义上,我可以想象你所描述的问题是应用程序运行的上下文。在人脸的层面上,这是多重折叠的,只取一个常数:

define('DEVELOPMENT_ENVIRONMENT', true);

即使很简单,很容易介绍,但代价很高。如果它已经是你的应用程序的一部分,首先试着理解它的含义是什么。

你有一个应用程序代码库,在它的某个地方-在具体的任何地方使用常数-如果这个常数是TRUEFALSE,你的代码分支就会被执行。

这本身是有问题的,因为这样的代码往往变得复杂难以调试。所以无论如何(常量、变量、函数、类),你首先应该减少防止使用这些结构。

老实说,使用(全局)常数对我来说并没有什么错,特别是与其他选择相比,首先在我看来,它是最可取的,因为它较少,不复杂,但相当直接。在当前的PHP版本中,您可以通过使用const关键字来声明它,从而将其转换为不那么动态的常量:

const DEVELOPMENT_ENVIRONMENT = TRUE;

这是这一小行代码的一个方面。另一个原因是它的抽象层次很低。如果您想为应用程序定义环境,那么说开发环境是真还是假是不明确的。相反,您通常有一个可以是不同类型的环境:

const ENVIRONMENT_UNSPECIFIED = 0;
const ENVIRONMENT_DEVELOPMENT = 1;
const ENVIRONMENT_STAGING     = 2;
const ENVIRONMENT_LIVE        = 3;
const ENVIRONMENT = ENVIRONMENT_DEVELOPMENT;

然而,这个小例子只是一个例子,让我的意思看起来有点模糊。它不能解决上面概述的一般问题下面的问题:

在全局级别上向应用程序引入上下文。这意味着组件(函数、类)中与任何全局(这里:DEVELOPMENT_ENVIRONMENT)相关的任何一行代码都不能再与全局状态解耦了。这意味着您编写的代码只能在应用程序的全局上下文中工作。如果您想要编写可重用的软件组件,这会阻碍您的工作。可重用性不仅意味着第二个应用程序,它已经意味着测试和调试。或者只是你软件的下一个修订版。正如你所想象的那样,它很快就会挡住你的路——或者让我们说,比你想要的更快。

所以这里的问题不是常量本身,而是更多地依赖于代码将运行的单一上下文,或者更好的说法是全局静态。当您希望在这里引入更好的更改时,您需要瞄准的目标是减少这个全局静态状态。如果你正在寻找替代方案,这一点很重要,因为它将帮助你做出更好的决策。

例如,而不是引入一组常数,我在最后的代码示例,找到你使用DEVELOPMENT_ENVIRONMENT的地方,想想为什么你把它放在那里,如果它是不可能删除它。因此,首先考虑是否需要(这些环境标志通常是一种气味,一旦在快速调试中需要,或者因为它被认为是"哦,多么实用"-然后在几周不使用的情况下在代码中腐烂)。在你考虑了它是否需要之后,你来到了它是需要的那一点,你需要找出为什么在那个地方需要它。它真的属于那里吗?难道它不能——就像你应该做的那样——变成一个参数吗?

根据定义,对象通常附带它们自己的上下文。如果您的日志记录器在开发过程中的行为与实际情况不同,那么这应该是一个配置,而不是应用程序代码中的某个地方的决定。如果您的应用程序总是有一个记录器,那么就注入它。应用程序代码只是记录。

所以你可以想象,这完全取决于许多不同的事情,如何以及何时可以防止这种情况。我只能建议你现在就找出答案,以减少总使用量。

对于我们在应用程序中遇到的常见场景,这里有一些实用的技巧。对于"根路径问题",您可以将相对路径__DIR__等魔法常数结合使用。例如,如果web浏览器的前端(例如index.php)需要指向托管代码的私有应用程序目录:

<?php
/**
* Turbo CMS - Build to race your website's needs to the win.
*
* Webroot Endpoint
*/
require(__DIR__ . '/../private/myapp/bootstrap.php');
然后,应用程序通常知道它是如何工作的,以及在哪里找到相对于自己的文件。如果你返回一些应用程序上下文对象(这必须而不是是全局的),你也可以注入webroot文件夹:
<?php
/**
* Turbo CMS - Build to race your website's needs to the win.
*
* Webroot Endpoint
*/
/* @var $turboAppContext TurboAppWebappContext */
$turboAppContext = require(__DIR__ . '/../private/myapp/bootstrap.php');
$turboAppContext->setWebroot(__DIR__);

现在,web服务器的上下文配置应用程序默认值。这实际上是一个至关重要的部分,因为它涉及到应用程序(但不是每个组件)内部的上下文字段。你不能阻止这个上下文。这就像抽象泄漏一样。您的应用程序运行在一个环境(称为"系统")中。但即便如此,你还是想让它尽可能地独立。

就像上面的DEVELOPMENT_ENVIRONMENT常数一样,这些点对于减少和找到它们的正确位置至关重要。另外,只允许非常特定的层设置输入值(更改上下文),并且只允许软件的某些高级层访问这些值。代码库的大部分应该在没有这些参数的情况下工作。你只能通过传递参数和而不是来控制访问。然后允许访问特定设置(在这个词的最佳含义中)的级别上的代码可以访问它-其他所有内容都没有参数。为了获得这种安全性,需要尽可能地杀死全局变量。

。重定向到另一个位置的功能需要当前请求的基url。它不应该从服务器变量中获取它们,而是基于一个抽象访问服务器变量的请求对象,这样你就可以替换这里的东西(例如,当你把应用程序移到前端代理之后——这并不总是最好的例子,但这是可能发生的)。如果您已经根据$_SERVER对软件进行了硬编码,那么您就需要在软件的某些阶段修改$_SERVER。您不希望这样,相反,您可以通过使用代表应用程序所需的特定功能的对象来摆脱这种(再次)全局静态状态(这里通过超全局变量,在全局常量旁边找到这些变量)。

既然我们在谈论web应用程序,看看Symfony的请求和响应抽象(它也被许多其他项目使用,使你的应用程序更加开放和流畅)。但这只是一个旁注。

所以无论你想基于什么来做决定,都不要被要打多少个字母所误导。当您开始考虑在开发软件时需要键入的全部字母时,这样做的好处是非常短视的。

相反,要理解你在什么地方引入了上下文,什么地方可以防止,什么地方不能。对于不能这样做的地方,请考虑将context作为参数而不是代码的"属性"。更流畅的代码使您的代码更可重用,更好的测试和更少的麻烦,当您转移到另一个平台。

如果您有很大的安装基础,这一点尤其重要。基于这些全局静态状态的代码很难维护:延迟发布、缓慢发布、令开发人员失望、繁重的开发。我们可以从中吸取教训,这些教训就是要理解语言的某些特征有哪些含义,以及何时使用它们。

我能给出的最好的规则是——我根本不是一个学术开发人员——将全球视为昂贵的。它可以是一个极好的捷径来建立一些东西,但你应该知道它的价格。这个领域很广,因为它不仅适用于面向对象编程,实际上也适用于过程代码。在面向对象编程中,存在许多教育材料,提供了防止全局静态状态的不同方法,因此我甚至可以说,这种情况有很好的文档记录。但是PHP并不是纯粹面向对象的,所以它并不总是像手边有一个对象那么容易——你可能首先需要引入一些对象(但是,也要看到已经可用的请求和响应抽象)。

所以我能给出的最好的建议,以提高你的代码在这个问题的上下文中是:坚持常数(可能与const关键字,使他们不那么动态和更恒定),然后只是尝试删除它们。正如在评论中所写的那样,PHP在跨平台文件访问方面做得很好,只需使用/作为目录分隔符,这很容易理解并且工作得很好。无论如何,尽量不要引入根路径常量——对于你编写的代码来说,它不应该是常量,而是在某种程度上是一个参数——它可以改变,例如在子请求或子应用中,它可以节省你的生命周期,而不是重新发明轮子。

困难的任务是保持事情简单。但这值得。

只需在vhost配置中放入一些服务器变量,并为每个选项准备不同的配置文件。使用apache,它将是(您将需要mod_env模块):

SetEnv ENVIRONMENT dev

然后在index中输入:

$configFileName = getenv ('ENVIRONMENT').'.ini';

现在只需加载该文件并确定给定值的所有应用程序行为。当然,如果你使用一些框架,你可以进一步简化它,但这将是一个良好的开端。

您可以将常量封装在类中,然后通过静态方法检索它:

if(Config::devMode()) {}
echo Config::baseUrl();

这样可以节省一行代码和一些内存,因为不需要实例化对象。

最新更新