PHP Magic Constants
What Are Magic Constants? In simple terms: Magic constants are special names, written with double underscores, that tell you where a piece of code is: which file, which folder, which line, which funct
What Are Magic Constants?
In simple terms: Magic constants are special names, written with double underscores, that tell you where a piece of code is: which file, which folder, which line, which function, or which class.
Technically: Magic constants look like constants but their value depends on the location in the source code where they are written. PHP replaces them with the correct value when it compiles the file. They are predefined by PHP, cannot be redefined, and (unlike normal constants) are case-insensitive — although they are always written in uppercase by convention.
Why They Exist
Code often needs information about itself:
- File paths: to include other files reliably, regardless of where the script was started from.
- Debugging and logging: to record which file, line, or method produced a message.
- Class names as strings: for factories, dependency injection, routing, and error messages, without typing names by hand.
Magic constants provide this information automatically, so it never gets out of date when files or classes are renamed or moved.
Overview
| Magic Constant | Value |
|---|---|
__LINE__ | The current line number in the file |
__FILE__ | The full path and file name of the current file (symlinks resolved) |
__DIR__ | The directory of the current file (same as dirname(__FILE__)) |
__FUNCTION__ | The name of the current function or method |
__CLASS__ | The fully qualified name of the current class |
__METHOD__ | The class and method name, e.g. App\Order::save |
__NAMESPACE__ | The current namespace (empty in the global namespace) |
__TRAIT__ | The fully qualified name of the current trait |
__PROPERTY__ | The name of the current property inside a property hook (PHP 8.4+) |
ClassName::class | The fully qualified class name as a string (not technically a magic constant, but resolved the same way) |
__LINE__, __FILE__, and __DIR__
Example
Assume this code is saved as /var/www/shop/public/info.php:
<?php
echo "Line: " . __LINE__ . "\n";
echo "File: " . __FILE__ . "\n";
echo "Directory: " . __DIR__ . "\n";
echo "File name only: " . basename(__FILE__) . "\n";
echo "Parent directory: " . dirname(__DIR__) . "\n";
Output
Line: 2
File: /var/www/shop/public/info.php
Directory: /var/www/shop/public
File name only: info.php
Parent directory: /var/www/shop
How It Works
__LINE__is replaced with the line number on which it appears (line 2 here).__FILE__is the absolute path of the file containing the code, even if that file was included by another file.__DIR__is the directory of that file, without a trailing slash (except for the root directory/).basename()returns the last part of a path;dirname()returns the parent directory.dirname(__DIR__, 2)goes up two levels.
On Windows, paths look like C:\xampp\htdocs\shop\public\info.php.
Reliable Includes With __DIR__
The Problem With Relative Paths
<?php
// public/index.php
require 'config/app.php';
A relative path such as 'config/app.php' is resolved against PHP's include_path and the current working directory — the directory from which PHP was started — not against the location of index.php. The same file may work in the browser but fail when run from a cron job, a test runner, or another directory.
The Solution
shop/
├── config/
│ └── app.php
├── src/
│ └── helpers.php
└── public/
└── index.php
<?php
// public/index.php
$projectRoot = dirname(__DIR__);
require $projectRoot . '/config/app.php';
require $projectRoot . '/src/helpers.php';
Why This Works
__DIR__is always the directory ofindex.phpitself:.../shop/public.dirname(__DIR__)is the project root:.../shop.- The final paths are absolute, so they work no matter where PHP was started from.
This is the standard way to include files in modern PHP code. Composer's autoloader is included the same way: require dirname(__DIR__) . '/vendor/autoload.php';.
Security note: Never build include paths from user input (
require $_GET['page'] . '.php'). That allows attackers to include unexpected files. Map allowed page names to known file paths instead. See PHP Include.
__FUNCTION__, __CLASS__, __METHOD__, __NAMESPACE__, and __TRAIT__
Example
Assume this file is /var/www/shop/src/Reports/SalesReport.php:
<?php
// File: /var/www/shop/src/Reports/SalesReport.php
namespace App\Reports;
trait Loggable
{
public function logContext(): string
{
return __TRAIT__ . ' used in ' . __CLASS__;
}
}
class SalesReport
{
use Loggable;
public function build(): void
{
echo "Namespace: " . __NAMESPACE__ . "\n";
echo "Class: " . __CLASS__ . "\n";
echo "Method: " . __METHOD__ . "\n";
echo "Function: " . __FUNCTION__ . "\n";
echo "Line: " . __LINE__ . "\n";
echo "Context: " . $this->logContext() . "\n";
}
}
(new SalesReport())->build();
echo "Class name: " . SalesReport::class . "\n";
Output
Namespace: App\Reports
Class: App\Reports\SalesReport
Method: App\Reports\SalesReport::build
Function: build
Line: 23
Context: App\Reports\Loggable used in App\Reports\SalesReport
Class name: App\Reports\SalesReport
What Each Constant Shows
| Constant | Result | Explanation |
|---|---|---|
__NAMESPACE__ | App\Reports | The namespace declared at the top of the file |
__CLASS__ | App\Reports\SalesReport | The fully qualified class name, including the namespace |
__METHOD__ | App\Reports\SalesReport::build | Class and method combined — useful in log messages |
__FUNCTION__ | build | Only the method name |
__TRAIT__ | App\Reports\Loggable | The trait in which the code is written |
__CLASS__ inside a trait | App\Reports\SalesReport | The class that uses the trait, not the trait itself |
Namespaces, classes, and traits are covered in the PHP OOP section. The important point here: these values are always correct and update automatically if you rename the class or move it to another namespace.
Outside Classes and Functions
<?php
function calculateShipping(): string
{
return __FUNCTION__;
}
echo calculateShipping() . "\n";
var_dump(__CLASS__);
var_dump(__NAMESPACE__);
Output
calculateShipping
string(0) ""
string(0) ""
Outside a class, __CLASS__ is an empty string; in the global namespace, __NAMESPACE__ is empty too.
ClassName::class
::class returns the fully qualified name of a class as a string, resolved at compile time using the current namespace and use imports.
Example
<?php
namespace App\Billing;
use App\Reports\SalesReport;
class Invoice {}
echo Invoice::class . "\n";
echo SalesReport::class . "\n";
$invoice = new Invoice();
echo $invoice::class . "\n";
Output
App\Billing\Invoice
App\Reports\SalesReport
App\Billing\Invoice
Why Use ::class Instead of Writing the Name
- No typos. A misspelled string such as
'App\Biling\Invoice'fails only at runtime; a misspelled class inInvoce::classis flagged by editors and static analysis tools. - Refactoring. IDEs rename
Invoice::classautomatically when the class is renamed. - Imports.
SalesReport::classresolves through theusestatement, so you do not repeat the full namespace.
::class on a class name does not require the class to exist or be loaded — it only resolves the name. Using ::class on an object ($invoice::class) is available since PHP 8.0 and is equivalent to get_class($invoice).
Common uses include registering classes in dependency injection containers, mapping routes to controllers, and writing log messages.
__PROPERTY__ (PHP 8.4+)
PHP 8.4 introduced property hooks, which run code when a property is read or written. Inside a hook, __PROPERTY__ contains the property's name.
Example
<?php
class Product
{
public string $name {
set(string $value) {
$value = trim($value);
if ($value === '') {
throw new InvalidArgumentException(__PROPERTY__ . ' cannot be empty.');
}
$this->name = $value;
}
}
}
$product = new Product();
$product->name = ' Desk Lamp ';
echo $product->name . "\n";
try {
$product->name = ' ';
} catch (InvalidArgumentException $e) {
echo $e->getMessage() . "\n";
}
Output
Desk Lamp
name cannot be empty.
The set hook trims and validates every assignment. Using __PROPERTY__ in the message means the same validation code can be copied to other properties without editing the property name. Property hooks are covered in Property Hooks (PHP 8.4).
Compile-Time Resolution: A Logging Pitfall
Magic constants are replaced where they are written, not where code is called. This matters for logging helpers.
Problem
<?php
// File: orders.php
function logMessage(string $message): void
{
printf("[%s:%d] %s\n", basename(__FILE__), __LINE__, $message);
}
logMessage('Order created');
logMessage('Payment received');
Output
[orders.php:5] Order created
[orders.php:5] Payment received
Both messages report line 5 — the line inside the helper — instead of the lines where logMessage() was called. The log is useless for finding where events happened.
Solution 1: Pass the Location Explicitly
<?php
// File: orders.php
function logHere(string $message, string $file, int $line): void
{
printf("[%s:%d] %s\n", basename($file), $line, $message);
}
logHere('Order created', __FILE__, __LINE__);
Output
[orders.php:8] Order created
The caller passes its own __FILE__ and __LINE__, which are resolved at the call site.
Solution 2: Read the Caller With debug_backtrace()
<?php
// File: orders.php
function logCaller(string $message): void
{
$caller = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
printf("[%s:%d] %s\n", basename($caller['file']), $caller['line'], $message);
}
logCaller('Payment received');
Output
[orders.php:10] Payment received
How It Works
debug_backtrace()returns information about the current call stack at runtime.- Element
[0]describes the call tologCaller()itself; itsfileandlineare the location of that call. DEBUG_BACKTRACE_IGNORE_ARGSskips collecting function arguments, and the limit1returns only one frame, which keeps the call cheap.
Logging libraries such as Monolog (used by Laravel and Symfony) handle this automatically, but understanding the difference between compile-time and runtime information explains why.
Practical Example: Consistent Error Messages
<?php
namespace App\Payments;
class PaymentGateway
{
public function charge(int $amountPaise, string $currency): string
{
if ($amountPaise <= 0) {
throw new \InvalidArgumentException(
sprintf('%s(): amount must be positive, %d given.', __METHOD__, $amountPaise)
);
}
if ($currency !== 'INR') {
throw new \InvalidArgumentException(
sprintf('%s(): unsupported currency "%s".', __METHOD__, $currency)
);
}
return 'Charged ₹' . number_format($amountPaise / 100, 2);
}
}
$gateway = new PaymentGateway();
foreach ([[49900, 'INR'], [0, 'INR'], [1000, 'USD']] as [$amount, $currency]) {
try {
echo $gateway->charge($amount, $currency) . "\n";
} catch (\InvalidArgumentException $e) {
echo 'Error: ' . $e->getMessage() . "\n";
}
}
Output
Charged ₹499.00
Error: App\Payments\PaymentGateway::charge(): amount must be positive, 0 given.
Error: App\Payments\PaymentGateway::charge(): unsupported currency "USD".
Step-by-Step Explanation
__METHOD__insertsApp\Payments\PaymentGateway::chargeinto each error message, so anyone reading logs immediately knows which method rejected the input.- If the class is renamed or moved to another namespace, the messages update automatically.
- The backslash in
\InvalidArgumentExceptionrefers to the built-in class in the global namespace, because the file declares its own namespace (App\Payments). Namespaces are covered in PHP Namespaces. - The loop uses array destructuring (
[$amount, $currency]) to test three cases: a valid charge and two invalid inputs.
Common Mistakes
Using Relative Paths for Includes
Effect: Files are not found when the script runs from a different directory (cron jobs, CLI tools, tests).
Fix: Build paths from __DIR__.
Expecting __LINE__ or __FILE__ in a Helper to Show the Caller
Effect: Logs always point to the helper.
Fix: Pass __FILE__ and __LINE__ from the caller, or use debug_backtrace().
Confusing __CLASS__ and __TRAIT__ Inside Traits
Effect: Expecting the trait name from __CLASS__.
Fix: Use __TRAIT__ for the trait's name and __CLASS__ for the class using it.
Hard-Coding Class Names as Strings
Effect: Typos and outdated names after refactoring.
Fix: Use ClassName::class.
Exposing Paths to Users
Effect: Error pages showing __FILE__ or __DIR__ reveal the server's directory structure.
Fix: Write such details to logs, not to responses (see Error Reporting in Production).
Best Practices
- Use
__DIR__(anddirname(__DIR__)) for everyrequireandincludepath. - Use
ClassName::classwhenever a class name is needed as a string. - Use
__METHOD__in exception messages and log entries to identify their source. - Remember that magic constants are resolved where they are written; use runtime tools for caller information.
- Write magic constants in uppercase for readability, even though PHP accepts any case.
Official reference: https://www.php.net/manual/en/language.constants.magic.php