advanced php language
Reflection
Reflection lets PHP code inspect other PHP code at runtime. You can inspect classes, methods, properties, parameters, attributes, types, visibility, and more.
Reflection is used by frameworks, service containers, serializers, routers, ORMs, test tools, static-analysis helpers, and developer tooling. It is powerful, but it should not be the first choice for ordinary business logic. Explicit code is usually easier to read and refactor.
Inspecting A Class
ReflectionClass gives information about a class.
<?php
declare(strict_types=1);
final class RegisterUser
{
public function __construct(
private Mailer $mailer,
) {
}
}
interface Mailer
{
public function send(string $email, string $message): void;
}
$class = new ReflectionClass(RegisterUser::class);
echo $class->getName() . PHP_EOL;
echo $class->isFinal() ? 'final' : 'not final';
echo PHP_EOL;
// Prints:
// RegisterUser
// final
Frameworks use this kind of information to discover classes and understand how to build them.
Inspecting Constructor Parameters
Service containers often inspect constructor parameters to autowire dependencies.
<?php
declare(strict_types=1);
interface Mailer
{
public function send(string $email, string $message): void;
}
final class RegisterUser
{
public function __construct(
private Mailer $mailer,
private string $appName,
) {
}
}
$class = new ReflectionClass(RegisterUser::class);
$constructor = $class->getConstructor();
foreach ($constructor?->getParameters() ?? [] as $parameter) {
$type = $parameter->getType();
echo $parameter->getName() . ': ' . ($type?->__toString() ?? 'mixed') . PHP_EOL;
}
// Prints:
// mailer: Mailer
// appName: string
The container can infer that Mailer is a dependency. It still needs configuration for which Mailer implementation to use and what string value should be passed for $appName.
Inspecting Attributes
Reflection is how code reads attributes.
<?php
declare(strict_types=1);
#[Attribute(Attribute::TARGET_METHOD)]
final readonly class Route
{
public function __construct(
public string $method,
public string $path,
) {
}
}
final class UserController
{
#[Route('GET', '/users')]
public function index(): string
{
return 'User list';
}
}
$method = new ReflectionMethod(UserController::class, 'index');
foreach ($method->getAttributes(Route::class) as $attribute) {
$route = $attribute->newInstance();
echo $route->method . ' ' . $route->path . PHP_EOL;
}
// Prints:
// GET /users
The attribute is metadata. Reflection lets runtime code read that metadata.
Invoking Methods With Reflection
Reflection can call methods dynamically, but this should be used carefully.
<?php
declare(strict_types=1);
final class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
}
$method = new ReflectionMethod(Calculator::class, 'add');
echo $method->invoke(new Calculator(), 2, 3) . PHP_EOL;
// Prints:
// 5
For normal code, $calculator->add(2, 3) is clearer. Reflection invocation is mainly useful for frameworks, test tools, and dynamic dispatch systems.
Visibility And Private Members
Reflection can inspect private properties and methods. That does not mean application code should bypass visibility.
Visibility expresses design. If you need reflection to change a private property in production code, the design probably needs a clearer public method.
Testing tools and serializers sometimes need controlled access, but treat that as infrastructure-level work.
Performance And Caching
Reflection has a cost. Frameworks often cache reflected metadata so they do not repeatedly inspect the same classes on every request.
Do not panic about using reflection where it belongs, but avoid reflection inside tight loops unless there is a clear reason and measurement.
What You Should Be Able To Do
After this lesson, you should be able to inspect a class, read constructor parameters, read attributes, and explain why reflection is common in frameworks but uncommon in ordinary business logic.
For junior work, this matters because many "magic" framework features are powered by reflection. Understanding that makes autowiring, attributes, routing, serializers, and testing tools less mysterious.
Practice
Practice: Inspect Constructor Parameters
Create a small PHP example that uses reflection to inspect a service constructor.
Task
Build:
- a
Mailerinterface - a
RegisterUserclass with a constructor that acceptsMailerand a string app name - reflection code that prints each constructor parameter name and type
Use strict types. Keep the expected output in the PHP code block as printed lines or comments.
Check Your Work
Confirm:
ReflectionClasstargets the service classgetConstructor()finds the constructor- each parameter name and type is printed
Afterward, explain how this relates to service-container autowiring.
Show solution
This solution uses ReflectionClass to inspect constructor dependencies.
<?php
declare(strict_types=1);
interface Mailer
{
public function send(string $email, string $message): void;
}
final class RegisterUser
{
public function __construct(
private Mailer $mailer,
private string $appName,
) {
}
}
$class = new ReflectionClass(RegisterUser::class);
$constructor = $class->getConstructor();
foreach ($constructor?->getParameters() ?? [] as $parameter) {
$type = $parameter->getType();
echo $parameter->getName() . ': ' . ($type?->__toString() ?? 'mixed') . PHP_EOL;
}
// Prints:
// mailer: Mailer
// appName: string
Service containers use this kind of reflection to see what a class needs. They can usually resolve class or interface dependencies with bindings, but scalar values such as strings often need explicit configuration.