определить () против const

658

В PHP, когда вы используете

define('FOO', 1);

и когда вы используете

const FOO = 1;

?

Каковы основные различия между этими двумя?

danjarvis
источник
4
О производительности (пустая трата времени с микрооптимизацией, как у меня), смотрите ответ : constв два раза быстрее, чем define. О времени загрузки страницы и использовании памяти: см. Этот вопрос и эту статью ... Смотрите также кое-что о кеше кода операции здесь .
Питер Краусс
2
Не смотрите только на текущий принятый ответ, но посмотрите на stackoverflow.com/a/3193704/1412157, поскольку это должен был быть принятый ответ
LucaM
1
Только что увидел уведомление для вашего комментария. Я обновил принятый ответ.
danjarvis

Ответы:

1039

Начиная с 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принимает статический скаляр (число, строку или другую константу , как true, false, null, __FILE__), в то время как define()принимает любое выражение. Начиная с PHP 5.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);
    }
  • consts всегда чувствительны к регистру, в то время как define()позволяет вам определять нечувствительные к регистру константы, передавая trueв качестве третьего аргумента (Примечание: определение нечувствительных к регистру констант не рекомендуется с PHP 7.3.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
}

Резюме

Если вам не нужен какой-либо тип условного или экспрессивного определения, используйте consts вместо define()s - просто для удобства чтения!

NikiC
источник
1
Как я знаю, в PHP 5.6 вы получите возможность использовать простые скалярные выражения даже с constязыковой конструкцией - см. Wiki.php.net/rfc/const_scalar_exprs
mabe.berlin
6
Еще одним преимуществом использования constявляется отсутствие кавычек, что означает, что он отформатирован так же, как и в вашей IDE.
rybo111
3
Это должно быть то, что в документации PHP. Это лучшее объяснение, которое я видел, особенно ту часть, которую большинство людей забывают (компиляция против времени выполнения).
Джеймс
4
define('a', $_GET['param']);, const b = a;работает отлично и получает значение, пока const c = $_GET['param'];недействительно. Действительно constли время компиляции? Я так не думаю ... (проверено на PHP 7.0.7)
mcserep
1
wiki.php.net/rfc/case_insensitive_constant_deprecation « В PHP 8.0: удалите возможность объявления нечувствительных к регистру констант. » для всех, кто рискнет здесь в будущее в отношении чувствительности к регистру
Scuzzy
196

До PHP 5.3 constне могли использоваться в глобальном масштабе. Вы можете использовать это только внутри класса. Это следует использовать, когда вы хотите установить какую-то постоянную опцию или параметр, относящийся к этому классу. Или, может быть, вы хотите создать какое-то перечисление.

defineможет использоваться для той же цели, но может использоваться только в глобальной области видимости. Его следует использовать только для глобальных настроек, которые влияют на все приложение.

Примером хорошего constиспользования является избавление от магических чисел. Посмотрите на константы PDO . Когда вам нужно указать тип выборки, вы должны напечатать PDO::FETCH_ASSOC, например. Если бы conts не использовались, вы бы в конечном итоге набрали что-то вроде 35(или что-то FETCH_ASSOCопределено как). Это не имеет смысла для читателя.

Примером правильного defineиспользования может быть указание корневого пути вашего приложения или номера версии библиотеки.

ryeguy
источник
12
Возможно, стоит упомянуть использование constс пространствами имен.
Salathe
31
Следует также отметить, что PHP5.3 может очень широко использовать const в глобальной области видимости.
Гордон
5.2 тоже может. Прелесть «против и против» в том, что вы должны поставить перед ними префикс имени класса. Их использование делает код более читабельным и красивым. И это бонус для меня.
выгодно
1
@ryeguy, но что после PHP 5.3, где const можно использовать глобально, как define?
andho
1
@andho, назови другой танец PHP: «шаг вперед», «шаг назад», танец «улучшений». Он. :)
Профессор Фалькен нарушил договор
37

Я знаю, что на этот вопрос уже дан ответ, но ни в одном из текущих ответов не упоминается пространство имен и то, как оно влияет на константы и определения.

Начиная с 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;

все еще будет вне.

GordonM
источник
8
Я не мог удержаться от вопроса: вы сознательно определили FORTY_TWOкак 54?
Punchlinern
9
«Шесть на девять? Сорок два? Я всегда говорил, что во вселенной что-то в корне не так» Посмотрите работы Дугласа Адамса, если вам нужно полное объяснение.
GordonM
2
Ой. Я знал, что 42 - это жизнь, вселенная и все остальное, но я пропустил Шесть из девяти частей. Спасибо за объяснение!
Punchlinern
20

define Я использую для глобальных констант.

const Я использую для констант класса.

Вы не можете defineв поле зрения класса, а с constвами можете. Излишне говорить, что вы не можете использовать constвне области видимости.

Кроме того, с const, это фактически становится членом класса, и с define, это будет перемещено в глобальную область.

Джейкоб Релкин
источник
8
Как отмечалось в других ответах, const можно использовать вне классов в PHP 5.3+
MrWhite
Только константное ключевое слово может быть использовано для создания константы пространства имен, а не только классов
Алиреза Рахмани Халили
16

Ответ 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 на самом деле не гарантирует этого.

Слартибартфаст
источник
1
Да, но BARи \foo\BARпросто не одинаковые константы. Я согласен, что это действительно сбивает с толку, но если вы также считаете, что такие вещи, как логика пространств имен, являются непротиворечивыми и что они constне define()похожи на макрос C ( #define), то у PHP может быть какое-то оправдание.
СЗ
1
Понятие const - это именно то, как оно должно вести себя - вы находитесь в пространстве имен! Вы хотите получить его без идентификатора пространства имен, а затем создать его вне пространства имен. Это также делает его совместимым с определением const в классе, даже если он использует другой синтаксис. Да, определение тоже соответствует, по-другому.
Джерард ONeill
12

Большинство из этих ответов неверны или рассказывают только половину истории.

  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
AwesomeBobX64
источник
1
PHP> = 5.6 В Zend Engine Compiler были внесены некоторые изменения, и const теперь обрабатывается по-другому.
Анкит Вишвакарма
6

Да, const определяются во время компиляции, и, поскольку никическим состояниям не может быть назначено выражение, как может определить define (). Но также const не может быть объявлен условно (по той же причине). то есть. Ты не сможешь это сделать:

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

В то время как вы могли бы с помощью define (). Таким образом, это не сводится к личным предпочтениям, есть правильный и неправильный способ использовать оба.

В качестве отступления ... Я хотел бы увидеть некоторый класс const, которому можно присвоить выражение, своего рода define (), который можно выделить в классы?

MrWhite
источник
5

Добавить ответ Никии. constможет использоваться в классах следующим образом:

class Foo {
    const BAR = 1;

    public function myMethod() {
        return self::BAR;
    }
}

Вы не можете сделать это с define().

Маркус Линд
источник
4

Никто ничего не говорит о php-doc, но для меня это тоже очень важный аргумент в пользу предпочтения const:

/**
 * My foo-bar const
 * @var string
 */
const FOO = 'BAR';
Николай Лубышев
источник
0

С помощью определения ключевого слова константы вы получите возможность учета регистра без учета регистра, а с помощью константного ключевого слова - нет.

define("FOO", 1, true);
echo foo; //1
echo "<br/>";
echo FOO; //1
echo "<br/>";

class A {
  const FOO = 1;
}

echo A::FOO; //valid
echo "<br/>";

//but

class B {
  define FOO = 1; //syntax error, unexpected 'define'
}

echo B::FOO; //invalid
GaziAnis
источник