【问题标题】:Getting base url, development variable, the best way, without using globals获取base url,开发变量,最好的方法,不使用全局变量
【发布时间】:2013-05-13 20:14:49
【问题描述】:

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

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;

有更好的方法吗?

【问题讨论】:

  • 定义常量是一个完美的解决方案。如果它有效,则无需更改它。您需要避免的是可以在代码中的任何位置更改的全局变量,或者最糟糕的是:下载的代码/插件。
  • 您通常不需要在那里定义DS。只需使用 / 它在 PHP 中是跨平台的。
  • 您建议用常量类名替换常量常量名称 - 我认为常量不那么说谎。例如,您可以使用包含数组的全局变量来代替类。然而,多亏了魔术常量,您通常不再需要 ROOT 定义。只需考虑将其完全从代码库中删除,尝试减少而不是掩盖它。
  • 所以最好有这个? ini_set('error_log', realpath(dirname(__FILE__)) . '/' . 'tmp/logs/');
  • 是的,我的代码似乎有点老了,我会使用 __DIR__ 。 . .哈哈哈..大有帮助!

标签: php xml url-routing realpath


【解决方案1】:

我认为像您这样的问题通常无法得到回答,但无论如何他们可能都应该得到答案。只是没有唯一的黄金法则或解决方案来处理这个问题。

在最简单的意义上,我可以想象你描述的问题是应用程序运行的上下文。在人脸层面,这是多方面的,只需要 one 常量:

define('DEVELOPMENT_ENVIRONMENT', true);

即使非常简单和容易介绍,它的价格也很高。如果它已经是您应用程序的一部分,请首先尝试了解其含义。

您有一个应用程序代码库,并且在其中的某个位置 - 具体而言,在使用该常量的任何地方 - 如果此常量为 TRUEFALSE,则执行代码的分支。

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

老实说,使用(全局)常量对我来说并没有那么错误,尤其是与替代方案相比,它首先在我看来是最可取的,因为它的位置更少,也不复杂,而是直截了当.在当前 PHP 版本中,您可以通过使用 const keyword 声明它来将其转换为动态较少的常量:

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__ 等魔术常量结合使用。例如,如果 webroot 中的前端(例如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 Turbo\App\WebappContext */
$turboAppContext = require(__DIR__ . '/../private/myapp/bootstrap.php');
$turboAppContext->setWebroot(__DIR__);

现在您的网络服务器的上下文配置应用程序默认值。这实际上是一个关键部分,因为它触及了应用程序内部(但不是每个组件)内在的上下文领域。你不能阻止这种情况。就像leaking abstractions 一样。您的应用程序运行在一个环境(称为“系统”)中。但即便如此,您仍希望使其尽可能独立。

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

例如重定向到另一个位置的功能需要当前请求的基本 URL。它不应该从服务器变量中获取它们,而是基于抽象访问服务器变量的请求对象,以便您可以在此处替换内容(例如,当您将应用程序移动到前端代理后面时 - 并不总是最好的例子但这可能发生)。如果您针对$_SERVER 对软件进行了硬编码,则需要在软件的某些阶段修改$_SERVER。您不希望这样,而是通过使用代表您的应用程序需要的特定功能的对象来摆脱这个(再次)全局静态状态(这里通过超全局变量,在全局常量旁边找到那些)。

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

因此,无论您想根据什么做出决定,都不要被输入多少个字母所误导。当您开始考虑在开发软件时需要输入的所有字母时,这样做的好处是非常短视的。

相反,要了解你在哪里引入上下文,在哪里可以阻止,在哪里不能。对于您不能的地方,请考虑将上下文作为参数而不是代码的“属性”。更流畅的代码可以让您在迁移到另一个平台时获得更多可重用代码、更好的测试和更少的麻烦。

如果您的安装基础很大,这一点尤其重要。在这些具有全局静态状态的基础上的代码维护起来是一团糟:延迟发布、爬行发布、失望的开发人员、繁重的开发。有一些教训要学习,这些教训是要了解该语言的某些特征具有哪些含义以及何时使用它们。

我能给出的最佳规则——而且我根本不是一个学术开发者——是认为全球化是昂贵的。它可能是建立某些东西的绝佳捷径,但是您应该知道它的价格。这个领域很广泛,因为这不仅适用于面向对象的编程,而且实际上也适用于过程代码。在面向对象编程中,存在许多提供不同方法来防止全局静态状态的教育材料,所以我什至会说那里的情况有很好的记录。但是 PHP 并不是纯粹的 OOP,所以它并不总是像手头有一个对象那么容易 - 您可能首先需要引入一些(但是,请参阅已经可用的请求和响应抽象)。

因此,在这个问题的上下文中,我可以给出的改进您的代码的真正最佳建议是:坚持使用常量(可能使用 const 关键字以使它们不那么动态且更加恒定)然后就尝试删除它们。正如在 cmets 中所写的那样,PHP 在跨平台文件访问方面做得非常好,只需使用 / 作为目录分隔符,这很好理解并且效果很好。尽量不要引入根路径常量 - 对于您编写的代码,这不应该是常量,而是在某个级别上的参数 - 它可以更改,例如在子请求或子应用程序中可以为您节省生命周期在重新发明轮子之前。

艰巨的任务是让事情变得简单。但这是值得的。

【讨论】:

  • 不仅要阅读,还要做到:) 如果您想正确,请将您的代码置于版本控制之下,这样您就可以更轻松地进行修改,而不必担心丢失较早的版本。不使用版本控制作为全局变量更加昂贵:)
  • 是的,我已经习惯了版本控制,也更容易分享它以获得一些建设性的批评。
【解决方案2】:

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

SetEnv ENVIRONMENT dev

然后在索引中使用类似的东西:

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

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 这个方法没有错。像您正在使用的常量和全局变量之间存在区别。全局变量很糟糕,常量则不然。全局变量的问题不在于它们是全局可见的,而是它们是全局可编辑的。
    • 恭喜,您成功地将全局变量替换为全局函数。现在这真的是一个伟大的成就(不,不是,这很讽刺)。此外,在 99.9% 的情况下,通常认为静态都是废话,尤其是在您在这里放置“参数”的情况下。
    • @hakre 实际上,我会说设置常量(不是全局变量)是最好的。这就是他们的目的。但我的建议是回答 Joey Salac Hipolito,他想摆脱他的常量,并假设我对他的项目不了解更多......
    • 您可能有良好的意图,但是,您的建议是废话。简短地说。你可能还没有看到它,但如果你真的认为全局常量是最好的,那就把它写成答案。我认为这样会更好地反映您自己的理解。
    猜你喜欢
    • 2017-12-30
    • 2011-11-14
    • 2017-12-11
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多