Constants (define vs const)
debt(d3/e3/b3/t5)
Closest to 'default linter catches the common case' (d3). The detection_hints list phpcs, phpstan, and rector — all standard PHP tooling that catches misuse patterns like define() where const is preferred, magic strings that should be constants, and naming convention violations. These are common-case catches via standard static analysis, not specialist one-off tools.
Closest to 'simple parameterised fix (replace pattern with safer alternative)' (e3). The quick_fix describes replacing define() with class constants or PHP 8.1 enums, which is a straightforward substitution. Rector can automate many of these replacements. However, replacing magic numbers or strings scattered across a codebase requires a systematic find-and-replace within a component, fitting e3 rather than a single one-line patch.
Closest to 'localised tax' (b3). The choice of define() vs const is mostly scoped: class constants affect the class and its consumers, and global define() constants add a small tax to the files that use them. The applies_to scope covers web, cli, and queue-worker contexts, but the structural impact is localised — it does not reshape the whole architecture or impose a persistent productivity drag across many work streams.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states the canonical wrong belief: developers treat define() and const as fully interchangeable, not realising that const is compile-time (usable in class bodies and namespaces) while define() is runtime (supports dynamic names, works inside conditionals). This is a documented, well-known PHP gotcha that intermediate developers eventually learn but beginners consistently get wrong.
Also Known As
TL;DR
Explanation
PHP has two ways to define constants: const NAME = value (compile-time, usable in class bodies, interfaces, and at the top level, but not inside functions or conditionals) and define('NAME', value) (runtime, supports variable names, works anywhere). Class constants (const) support visibility modifiers since PHP 7.1 and typed constants since PHP 8.3. Use const for class-level constants and top-level application configuration; use define() when the constant name or value must be computed at runtime. Constants are globally accessible by name and cannot be redefined — prefer class constants or enums for namespaced, typed constant groups.
Common Misconception
Why It Matters
Common Mistakes
- Using define() inside functions or conditionally — prefer const at the top level for clarity and performance.
- Class constants not declared final in PHP 8.1 — subclasses can override them, violating the 'constant' contract.
- Using magic numbers instead of named constants — the meaning is lost and updates require searching the whole codebase.
- Naming constants with camelCase instead of SCREAMING_SNAKE_CASE — breaks PHP convention and reduces readability.
Code Examples
// Magic number — meaning unknown:
if ($status === 3) { /* What is 3? */ }
// Named constant:
const ORDER_STATUS_SHIPPED = 3;
if ($status === ORDER_STATUS_SHIPPED) { /* Clear */ }
// Non-final class constant — overrideable:
class Config {
const TIMEOUT = 30; // Should be: final const TIMEOUT = 30; (PHP 8.1+)
}
// define() — runtime, works anywhere
define('MAX_UPLOAD', 10 * 1024 * 1024);
define('APP_ENV', getenv('APP_ENV') ?: 'production');
// const — compile-time, preferred in classes/namespaces
class Order {
const STATUS_PENDING = 'pending';
const STATUS_PAID = 'paid';
const STATUS_CANCELLED = 'cancelled';
}
echo Order::STATUS_PAID;
// PHP 8.3 — typed class constants
class Config {
const string VERSION = '2.4.1';
const int MAX_RETRIES = 3;
}
// Useful built-in constants:
// PHP_EOL, PHP_INT_MAX, PHP_INT_SIZE
// DIRECTORY_SEPARATOR, PATH_SEPARATOR