Reflection API
debt(d7/e5/b5/t5)
Closest to 'only careful code review or runtime testing' (d7). Blackfire (listed) can detect performance cost in hot paths, but no linter flags reflection misuse by default; phpstan won't catch unchecked reflection use. Usually requires profiling or review.
Closest to 'touches multiple files / significant refactor in one component' (e5). Removing reflection from hot paths typically requires adding metadata caching layers or replacing with compiled containers — not a one-liner per quick_fix guidance.
Closest to 'persistent productivity tax' (b5). applies_to spans web/cli/queue and reflection often becomes load-bearing infrastructure in DI/ORM layers; touches many work streams but doesn't fully define system shape.
Closest to 'notable trap, documented gotcha' (t5). The misconception (only for framework devs) plus the non-obvious performance cost and encapsulation-breaking behavior are well-known gotchas most PHP devs eventually learn.
Also Known As
TL;DR
Explanation
The PHP Reflection API (ReflectionClass, ReflectionMethod, ReflectionProperty, ReflectionFunction, ReflectionParameter) enables runtime inspection of code structure — reading type declarations, attributes, docblocks, visibility, and default values without invoking the code. It underpins dependency injection containers (resolving constructor parameters), ORM hydration, serialisation libraries, and testing frameworks. Reflection is powerful but has a performance cost — production DI containers cache reflection results. PHP 8.0 Attributes provide a structured, performant alternative to docblock-parsed metadata.
Common Misconception
Why It Matters
Common Mistakes
- Using Reflection in hot code paths without caching results — ReflectionClass instantiation is expensive.
- Not caching reflected class metadata in a DI container — reflecting the same class on every request adds measurable overhead.
- Using Reflection to access private members of other classes in production code — breaks encapsulation.
- Not using PHP 8 Attributes instead of docblock parsing when possible — native attributes are faster and type-safe.
Code Examples
// Uncached reflection in a hot path:
function resolve(string $class): object {
$ref = new ReflectionClass($class); // Expensive — not cached
$params = $ref->getConstructor()->getParameters();
// ... inject dependencies
}
// Inspect class structure at runtime
$ref = new ReflectionClass(OrderService::class);
echo $ref->getName(); // 'App\Domain\OrderService'
echo $ref->getShortName(); // 'OrderService'
$methods = $ref->getMethods(ReflectionMethod::IS_PUBLIC);
$constructor = $ref->getConstructor();
// Read PHP 8.0+ attributes:
foreach ($ref->getMethods() as $method) {
foreach ($method->getAttributes(Route::class) as $attr) {
$route = $attr->newInstance();
echo $route->path;
}
}
// Instantiate without constructor (DI containers, test fixtures):
$instance = $ref->newInstanceWithoutConstructor();