Avatar ·

PHP: define() vs const — в чем разница и что лучше использовать?

Всем привет! Я пишу на PHP около года, и меня постоянно мучает один вопрос. Я вижу, что в разных проектах константы объявляют по-разному: одни используют define(), другие — ключевое слово const. Например, в старых фреймворках я часто вижу такой код: ```php define('DB_HOST', 'localhost'); define('DB_NAME', 'mydb'); ``` А в современных проектах на Laravel или Symfony — такое: ```php class Config { const API_KEY = 'abc123'; const TIMEOUT = 30; } ``` Или даже просто в глобальной области видимости: ```php const MAX_USERS = 100; ``` Я прочитал PSR-1 и PSR-12, но там написано довольно размыто. Вроде бы рекомендуется использовать const, но не объясняется почему. Я попробовал оба варианта — и оба работают. Но меня смущает, что define() позволяет задавать константы внутри блоков if, а const — нет. И еще я слышал, что в новых версиях PHP const стал быстрее. Это правда? Подскажите, пожалуйста, в каких ситуациях какой способ лучше? Есть ли какие-то практические отличия, кроме синтаксиса? И что считается современным стандартом? Заранее спасибо! <<>>

Основные различия между define() и const

Давайте разберем это раз и навсегда. Оба подхода создают константы, но у них есть ключевые отличия, которые влияют на выбор в конкретной ситуации.

1. Время определения

const — это языковая конструкция, которая обрабатывается на этапе компиляции. define() — это функция, которая выполняется в рантайме (во время исполнения скрипта).

Это главное отличие. Из-за этого const нельзя использовать внутри циклов, условий или функций:

// ❌ Ошибка: const нельзя использовать внутри if
if (true) {
    const TEST = 'value'; // Parse error
}

// ✅ define() работает в любом месте
if (true) {
    define('TEST', 'value'); // Работает
}

// ❌ const нельзя в цикле
for ($i = 0; $i < 5; $i++) {
    const ITEM = $i; // Ошибка
}

// ✅ define() можно
for ($i = 0; $i < 5; $i++) {
    define('ITEM_' . $i, $i); // Работает
}

2. Область видимости

const всегда объявляется в текущем пространстве имен. define() по умолчанию тоже, но в PHP 7+ можно явно указать пространство имен:

namespace MyApp;

// const — всегда в текущем namespace
const VERSION = '1.0'; // Полное имя: MyApp\VERSION

// define() — аналогично, если не указано иное
define('APP_NAME', 'MyApp'); // Глобальная константа, не в namespace
define('MyApp\DEBUG', true); // Явно указываем namespace (PHP 7+)

3. Типы значений

const поддерживает только скалярные типы и массивы (начиная с PHP 5.6). define() — то же самое, но с PHP 7+ может принимать любые выражения:

// const — только константные выражения
const PI = 3.14159;
const FRUITS = ['apple', 'banana']; // Работает с PHP 5.6+
// const RESULT = 1 + 2; // Работает (константное выражение)

// define() — может использовать переменные и выражения
$prefix = 'THEME_';
define($prefix . 'COLOR', 'blue'); // Динамическое имя

// define() с объектами? Только если это константное выражение
define('ARRAY_EXAMPLE', [1, 2, 3]); // Работает

4. Производительность

Да, const быстрее, потому что обрабатывается на этапе компиляции. define() — это вызов функции в рантайме. Разница заметна только при тысячах констант, но в современном PHP это редкость. Для обычных проектов разница в производительности несущественна.

5. Современные стандарты (PSR-1, PSR-12)

PSR-1 рекомендует использовать const для констант классов. PSR-12 говорит, что ключевое слово const должно быть в верхнем регистре. Оба стандарта молчат про define() для глобальных констант — это наследие старого PHP.

Практические рекомендации

  • В классах — всегда используйте const. Это современно, читаемо и соответствует PSR.
  • Глобальные константы — предпочтительнее const, если вам не нужно динамическое имя или условие.
  • Динамические имена (например, с префиксами) — только define().
  • Условное определение (например, в зависимости от окружения) — только define().
  • Внутри функций/методов — define(), так как const там не разрешен.

Пример правильного подхода

// Современный стиль: const для классов и глобальных констант
namespace App\Config;

const SITE_NAME = 'MySite';
const MAX_UPLOAD_SIZE = 10 * 1024 * 1024; // Константное выражение

class Database {
    const HOST = 'localhost';
    const PORT = 3306;
    const NAME = 'mydb';
}

// Если нужно динамическое имя — define()
$env = getenv('APP_ENV') ?: 'prod';
define('APP_ENVIRONMENT', $env);

// Условное определение
if (!defined('APP_DEBUG')) {
    define('APP_DEBUG', false);
}

Итог

В 2024 году используйте const везде, где это возможно. define() оставьте для случаев, когда нужно динамическое имя, условное определение или определение внутри блока кода. Это и есть современный стандарт де-факто.

<<>>

Дополнение: define() с PHP 8.1+ и переопределение констант

Стоит добавить важный нюанс про переопределение констант. const нельзя переопределить — это вызовет фатальную ошибку. define() тоже по умолчанию нельзя, но есть хитрость:

// const — ошибка при повторном объявлении
const VERSION = '1.0';
const VERSION = '2.0'; // Fatal error

// define() — тоже ошибка
define('VERSION', '1.0');
define('VERSION', '2.0'); // Notice: Constant already defined

// Но можно проверить через defined()
if (!defined('VERSION')) {
    define('VERSION', '1.0');
}

В PHP 8.1+ появилась возможность использовать define() с флагом case_insensitive (третий параметр, который делает константу регистронезависимой). Однако это считается устаревшей практикой и не рекомендуется к использованию.

Еще один момент: const в классах можно объявлять с модификаторами видимости (public, protected, private) с PHP 7.1+. Это удобно для инкапсуляции:

class Config {
    public const PUBLIC_KEY = 'pub'; // Доступно всем
    protected const INTERNAL_KEY = 'int'; // Только класс и наследники
    private const SECRET = 'sec'; // Только внутри класса
}

// define() такой возможности не дает — все константы глобальные

Поэтому в объектно-ориентированном коде const — это единственный правильный выбор.