PHP | define() vs. const

767
在PHP中,您可以通过两种方式声明常量:
  1. 使用 define 关键字

define('FOO', 1);
  • 使用 const 关键字

  • const FOO = 1;
    

    • 这两者之间的主要区别是什么?
    • 何时以及为什么应该使用其中之一,何时使用另一个?

    11
    关于性能(像我这样微观优化是浪费时间),请参考这个答案constdefine 快两倍。关于页面加载时间和内存使用情况,请参考这个问题这篇文章... 还可以在这里了解有关 opcode 缓存的信息。 - Peter Krauss
    9个回答

    1186

    从PHP 5.3开始,有两种方法可以定义常量:一种是使用const关键字,另一种是使用define()函数:

    const FOO = 'BAR';
    define('FOO', 'BAR');
    

    这两种方法的根本区别在于,const 在编译时定义常量,而 define 在运行时定义常量。这就导致了 const 的大部分劣势。某些 const 的劣势包括:

    • const 不能用于有条件地定义常量。要定义全局常量,它必须在最外层作用域中使用:

       if (...) {
           const FOO = 'BAR';    // Invalid
       }
       // but
       if (...) {
           define('FOO', 'BAR'); // Valid
       }
      

      你为什么要这样做呢?一个常见的应用是检查该常量是否已经定义:

       if (!defined('FOO')) {
           define('FOO', 'BAR');
       }
      
    • const关键字可以接受静态标量值(数字,字符串或其他常量,如truefalsenull__FILE__),而define()函数可以接受任何表达式。自PHP5.6以来,const也允许使用常量表达式:

       const BIT_5 = 1 << 5;    // Valid since PHP 5.6 and invalid previously
       define('BIT_5', 1 << 5); // Always valid
      
    • const 关键字只接受纯常量名称,而 define() 函数可接受任何表达式作为名称。这使得可以做到像下面这样的事情:

    •  for ($i = 0; $i < 32; ++$i) {
           define('BIT_' . $i, 1 << $i);
       }
      
    • const 始终区分大小写,而 define() 可以通过将第三个参数传递为 true 来定义不区分大小写的常量(注意:PHP 7.3.0 版本起定义不区分大小写的常量已被弃用,并在 PHP 8.0.0 中删除):

       define('FOO', 'BAR', true);
       echo FOO; // BAR
       echo foo; // BAR
      

    因此,那是不好的事情的一面。现在让我们看看为什么我个人总是使用 const,除非出现上述情况之一:

    • const 仅仅读起来更加清晰。它是语言结构而不是函数,并且与你在类中定义常量的方式一致。

    • const 是一种语言结构,可以被自动化工具进行静态分析。

    • const 在当前命名空间中定义了一个常量,而 define() 必须传递完整的命名空间名称:

     namespace A\B\C;
     // To define the constant A\B\C\FOO:
     const FOO = 'BAR';
     define('A\B\C\FOO', 'BAR');
    
    自 PHP 5.6 以来,const 常量也可以是数组,而 define() 目前不支持数组。然而,在 PHP 7 中,数组将适用于两种情况。
     const FOO = [1, 2, 3];    // Valid in PHP 5.6
     define('FOO', [1, 2, 3]); // Invalid in PHP 5.6 and valid in PHP 7.0
    
    最后请注意,const也可以在类或接口中使用来定义类常量或接口常量。不能使用define来实现这一目的:
    class Foo {
        const BAR = 2; // Valid
    }
    // But
    class Baz {
        define('QUX', 2); // Invalid
    }
    

    摘要

    除非你需要任何类型的条件或表达式定义,否则为了可读性使用const而不是define()


    2
    据我所知,使用PHP 5.6,您将有可能使用简单的标量表达式,甚至可以与“const”语言结构一起使用-请参见https://wiki.php.net/rfc/const_scalar_exprs。 - mabe.berlin
    11
    使用const的另一个好处是不需要引号,这意味着它在IDE中使用时格式相同。 - rybo111
    11
    这应该是PHP文档中的内容。这是我看过的最好的解释,特别是大多数人会忽略的部分(编译与运行时)。 - James
    5
    define('a', $_GET['param']); 翻译为:定义一个常量 'a',其值为 $_GET['param']。const b = a; 翻译为:将常量 b 的值设置为常量 a 的值,运行良好。const c = $_GET['param']; 翻译为:将常量 c 的值设置为 $_GET['param'],无效。 “const” 是否真的是编译时常量?我几乎不这样认为…(在 PHP 7.0.7 上进行了测试)。 - mcserep
    2
    在 PHP 8.0 中,将删除声明不区分大小写常量的可能性。对于未来涉及大小写敏感性问题的其他人,请注意。 - Scuzzy
    显示剩余4条评论

    199

    在 PHP 5.3 以前,const 不能在全局范围内使用。你只能在类内部使用它。当你想设置某些与该类相关的常量选项或设置时,应该使用它。或者可能你想创建某种枚举。

    define 可以用于同样的目的,但它只能在全局范围内使用。它应该仅用于影响整个应用程序的全局设置。

    好的 const 使用示例是消除魔术数字。看一下 PDO 的常量。当需要指定获取类型时,例如,你将输入 PDO::FETCH_ASSOC。如果不使用 const,则会输入类似于 35(或任何定义为 FETCH_ASSOC 的内容)。这对读者没有意义。

    好的 define 使用示例是指定应用程序的根路径或库的版本号。


    13
    有关命名空间使用 const 的内容可能值得一提。 - salathe
    32
    值得注意的是,PHP5.3可以在全局范围内非常方便地使用const。 - Gordon
    5.2同样可以。常量与宏定义的优点在于必须使用类名作为前缀。使用它们可以使代码更易读、更美观。这对我来说是一个额外的奖励。 - lucifurious
    1
    @ryeguy 但是在 PHP 5.3 之后,const 可以像 define 一样全局使用,那怎么办? - andho
    1
    @andho,又可以把这归功于 PHP 的一步向前、一步向后的“改进”之舞了。哈哈。 :) - Prof. Falken

    41

    我知道这个问题已经有了答案,但是目前的回答中没有提到命名空间及其对常量和定义的影响。

    从PHP 5.3开始,常量和定义在大多数方面都是相似的。然而,仍存在一些重要的区别:

    • 常量不能由表达式定义。const FOO = 4 * 3;不起作用,但define('CONST', 4 * 3);可以。
    • define函数传递的名称必须包含要定义的命名空间。

    下面的代码应该说明这些差异。

    namespace foo 
    {
        const BAR = 1;
        define('BAZ', 2);
        define(__NAMESPACE__ . '\\BAZ', 3);
    }
    
    namespace {
        var_dump(get_defined_constants(true));
    }
    

    用户子数组的内容将为['foo\\BAR' => 1, 'BAZ' => 2, 'foo\\BAZ' => 3]

    === 更新 ===

    即将发布的PHP 5.6将允许在const中使用更多灵活性。 只要这些表达式由其他常量或文字组成,就可以定义常量。 这意味着以下内容应该在5.6中有效:

    const FOOBAR = 'foo ' . 'bar';
    const FORTY_TWO = 6 * 9; // For future editors: THIS IS DELIBERATE! Read the answer comments below for more details
    const ULTIMATE_ANSWER = 'The ultimate answer to life, the universe and everything is ' . FORTY_TWO;
    

    不过,您仍然无法使用变量或函数返回值来定义常量,因此

    const RND = mt_rand();
    const CONSTVAR = $var;
    

    仍将不在。


    9
    我忍不住要问一下:你是否有意将FORTY_TWO定义为54? - Punchlinern
    10
    "六乘九?四十二?我一直说宇宙里有某些根本性的错误。" 如果需要完整解释,可以查看道格拉斯·亚当斯的作品。 - GordonM
    2
    哦,我知道42是生命、宇宙和一切的答案,但我错过了6乘以9的部分。感谢您的解释! - Punchlinern

    26

    define 是用于全局常量的定义。

    const 则用于类常量的定义。

    你不能在类作用域内使用 define,但可以使用 const

    而且,使用 const 定义的常量实际上成为了类的成员;而使用 define,常量将被推入全局作用域。


    10
    如其他答案中所述,在 PHP 5.3+ 中,const 可以在类外部使用。 - MrWhite
    1
    只有 const 关键字可以用来创建命名空间常量,而不仅仅是类。 - Alireza Rahmani Khalili

    23

    20

    NikiC的答案是最好的,但是让我补充一个关于使用命名空间时不太明显的警告,以免出现意外行为。需要记住的是,除非将命名空间作为定义标识符的一部分显式添加到定义中,否则定义始终位于全局命名空间中。其中不太明显的是,命名空间标识符优先于全局标识符。所以:

    <?php
    namespace foo
    {
      // Note: when referenced in this file or namespace, the const masks the defined version
      // this may not be what you want/expect
      const BAR = 'cheers';
      define('BAR', 'wonka');
    
      printf("What kind of bar is a %s bar?\n", BAR);
    
      // To get to the define in the global namespace you need to explicitely reference it
      printf("What kind of bar is a %s bar?\n", \BAR);
    }
    
    namespace foo2
    {
      // But now in another namespace (like in the default) the same syntax calls up the 
      // the defined version!
      printf("Willy %s\n", BAR);
      printf("three %s\n", \foo\BAR);  
    }
    ?>
    

    产生:

    What kind of bar is a cheers bar? 
    What kind of bar is a wonka bar?
    willy wonka 
    three cheers
    

    在我看来,这使得整个const概念变得不必要的混乱,因为在其他数十种语言中,const的想法是无论在代码的哪里都是相同的,而PHP并不真正保证这一点。


    2
    是的,但 BAR\foo\BAR 只是不同的常量。我同意这真的很令人困惑,但如果你考虑到命名空间逻辑保持一致,以及 constdefine() 都不像 C 的 (#define),那么 PHP 就有一些借口了。 - Sz.
    3
    "const" 的概念在命名空间中的行为 恰当 -- 因为你正在命名空间内!如果想要不带命名空间标识符的 const,则应该在命名空间外定义。这样也可以使得它和类中定义 const 的方式保持一致,即使语法不同。是的,使用不同语法的 "define" 也是一致的,只是方式不同而已。 - Gerard ONeill

    15

    大多数答案是错误的或只讲述了故事的一部分。

    1. 您可以使用命名空间来限定常量的作用域。
    2. 您可以在类定义之外使用 "const" 关键字。但是,就像在类中一样,使用 "const" 关键字赋值的值必须是常量表达式。

    例如:

    const AWESOME = 'Bob'; // Valid
    

    不好的例子:

    const AWESOME = whatIsMyName(); // Invalid (Function call)
    const WEAKNESS = 4+5+6; // Invalid (Arithmetic) 
    const FOO = BAR . OF . SOAP; // Invalid (Concatenation)
    

    使用define()来创建变量常量,如下所示:

    define('AWESOME', whatIsMyName()); // Valid
    define('WEAKNESS', 4 + 5 + 6); // Valid
    define('FOO', BAR . OF . SOAP); // Valid
    

    2
    PHP >=5.6在Zend Engine编译器中进行了一些更改,现在const的处理方式也有所不同。 - Ankit Vishwakarma

    7

    在NikiC的回答上补充一点。在类中,可以按以下方式使用const

    class Foo {
        const BAR = 1;
    
        public function myMethod() {
            return self::BAR;
        }
    }
    

    使用define()无法实现此功能。


    6

    是的,const在编译时被定义,并且如nikic所述,不能分配表达式,因为define()可以。但是也由于同样的原因,无法有条件地声明const。也就是说,你不能这样做:

    if (/* some condition */) {
      const WHIZZ = true;  // CANNOT DO THIS!
    }
    

    与使用define()相比,你可以使用。

    所以,这并不仅是个人喜好的问题,使用两者都有正确和错误的方式。

    另外...我希望能看到某种类常量,可以分配一个表达式,一种可以隔离到类的define()?


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