PHP全局常量是一种好的现代开发实践吗?

16
我正在处理一个规模相当大的PHP代码库的新项目。该应用程序使用了很多PHP常量(例如define('FOO', 'bar')),特别是用于数据库连接参数之类的内容。这些常量都定义在一个单独的配置文件中,由几乎每个类直接调用require_once()
几年前这样做完全没有问题,但自从我开始进行单元测试后,这种紧密耦合的方式让我感到很不舒服。这些常量看起来像是全局变量,并且在整个应用程序代码中被直接引用。
这还是一个好主意吗?将这些值复制到一个对象中,并使用此对象(即Bean - 我说了)通过依赖注入将它们传递给与数据库交互的类,这样做是否合理?这样做会否破坏PHP常量的任何好处(例如速度等)?
我考虑的另一种方法是为测试创建单独的配置PHP脚本。我仍然需要找到一种方法让测试类使用沙盒配置脚本而不是全局配置脚本。这仍然感觉很脆弱,但可能需要对整个应用程序进行更少的修改。

2
#define CONSTANTS 'are fine' 的翻译是什么? - Marc B
这些常量是/曾经是(超级)全局变量,只能设置一次。这就是为什么它们被用于配置 - 不是为了测试原因,而是(因为没有进行测试)确保它们不会被其他热火朝天的代码更改 - 只是为了确保代码的任何部分都不会... ;) - 因此选择适合工作的正确工具非常重要。逐步将遗留代码放入测试中是一种艺术。删除常量可以成为其中的一部分。 - hakre
1
我非常尊重地不同意,@Lasse。我发现这些答案非常有用。我可以通过阅读文档找到事实 - 我需要经验丰富的意见和理由来帮助澄清我对实际操作的理解。 - bpanulla
我稍微改了问题的措辞,以使其更专注于实际问题。 - bpanulla
1
好的,我已经确定了重新开放它的三个投票。我并不是说关闭它是/曾经是错误的,但是社区已经发表了意见。 - Lasse V. Karlsen
显示剩余4条评论
6个回答

6

在我看来,常量只应在两种情况下使用:

  • 实际的常量值(即永远不会改变的东西,例如SECONDS_PER_HOUR)。
  • 与操作系统相关的值,只要常量可以在应用程序中透明地使用,在任何可能的情况下都可以使用。

即使在这种情况下,我也会重新考虑是否更适合使用类常量,以避免污染常量空间。

在您的情况下,我认为常量不是一个好的解决方案,因为您将想要根据使用位置提供替代值。


1
一个例子是php文件的文件扩展名 define('EXT', '.php'),它不会在页面之间改变,并且对整个项目都是全局的。 - Xeoncross
这确实帮助我找到了问题的根源。我认为问题不在于所涉及的常量是常量,而在于它们是全局的,这才真正导致了问题。 - bpanulla
嗯...如果您不污染常量空间,那么您就有一个未使用的空间。您没有为任何事情保存它:您只是失去了一种语言特性(我尊重选择不使用define的选择)。 - Dereckson

5
这些常量像全局变量一样,被直接引用[...],将这些值复制到对象中,并通过依赖注入传递它们是否合理?当然!我甚至会更进一步地说,应该避免使用类常量。因为它们是公共的,暴露了内部细节并且是API,所以你不能轻易地改变它们而不会因紧密耦合而导致破坏现有应用程序。配置对象更加合理(只需不要将其设置为单例)。另请参见:

1
友情建议,更好地解释你的第一句话。问题的标题和措辞有些矛盾。标题问“常量好吗?”,但正文更像是在问“常量不好吗?”很难确定你站在哪一边。 - Mike B
@Mike 如果没有表述清楚,抱歉:“绝对”是指OP的担忧常量是全局状态以及使用配置对象的想法。 - Gordon

4
为了回答这个问题,重要的是讨论正在编写的代码的风格。
PHP 5包括许多有用的OOP功能之一是类常量。如果您使用面向对象的方法,而不是污染全局命名空间或担心覆盖常见常量,则应该使用类常量。
最终,FOO_BAR可以是FOO::BAR,这取决于您希望定义常量的范围。
如果您正在编写更多过程化风格的程序,或将过程化与一些类混合使用,全局常量则不是问题。如果您正在使用的代码由于使用的常量而变得难以管理,请尝试改变某些东西。否则,不必担心它。
此外,类常量不允许您使用函数返回值,全局常量则可以。当您有一个在程序范围内永远不会更改但需要生成的值时,这非常有用。

2

使用常量来存储数据库连接信息是完全可以的。这样可以避免在对象本身中进行硬编码,而且由于是只读的,您无法覆盖这些值。

我不喜欢在对象中硬编码我的设置,因为事情可能会发生变化,但如果您想这样做,那也同样有效。


2
在单元测试时,这成为了一个主要问题。最好使用依赖注入。 - alexantd

1
如果您使用的是PHP 5.3或更高版本,可以使用namespace
http://www.php.net/manual/en/language.namespaces.php 它适用于const variable = 'something';
不幸的是,它不能与define('variable','something');一起使用。
命名空间中的全局变量是封装的。在某些情况下,这比拥有对象更好。

0

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