brick/reflection
Low-level PHP 8.1+ helpers extending native reflection. ReflectionTools can list all instance methods/properties across inheritance (including private parents), get class hierarchies, and create reflections for any callable with meaningful function names.
Installation:
composer require brick/reflection
Ensure your project uses PHP 8.1+ (required since v0.6.0).
First Use Case: Inspect a class’s full method hierarchy (including private/parent methods):
use Brick\Reflection\ReflectionTools;
$tools = new ReflectionTools();
$methods = $tools->getClassMethods(new ReflectionClass(\App\Models\User::class));
foreach ($methods as $method) {
echo $method->getName() . "\n";
}
Key Entry Points:
ReflectionTools: For method/property introspection, type extraction, and signature export.ImportResolver: For resolving class names in annotations (e.g., @param App\Models\User $user).getClassMethods()/getClassProperties() to traverse inheritance chains.$tools = new ReflectionTools();
$class = new ReflectionClass(\App\Services\PaymentProcessor::class);
$methods = $tools->getClassMethods($class);
foreach ($methods as $method) {
$signature = $tools->exportFunctionSignature($method);
echo "$signature\n\n";
}
$container->resolving(function ($object, $abstract) {
if ($object instanceof \App\Contracts\Logger) {
$tools = new ReflectionTools();
$methods = $tools->getClassMethods(new ReflectionClass($object));
// Log method signatures for debugging
}
});
ImportResolver to resolve class names in annotations (e.g., for Doctrine-like ORM mappings).@Route attribute:
$resolver = new ImportResolver(new ReflectionClass(\App\Http\Controllers\UserController::class));
$routeClass = $resolver->resolve('Route'); // Resolves to fully qualified name
exportFunctionSignature() with ReflectionMethod to validate or generate dynamic calls.$method = new ReflectionMethod(\App\Services\Cache::class, 'store');
$signature = $tools->exportFunctionSignature($method);
// Use signature to validate user input before calling $method->invoke(...)
getClassHierarchy() to traverse parent classes (e.g., for trait-based logic).$hierarchy = $tools->getClassHierarchy(new ReflectionClass(\App\Models\Post::class));
foreach ($hierarchy as $classReflection) {
foreach ($classReflection->getMethods() as $method) {
if ($method->implementsInterface(\App\Contracts\Publishable::class)) {
// Handle publishable methods
}
}
}
Service Provider Bootstrapping:
ReflectionTools instance for reuse:
$this->app->singleton(ReflectionTools::class, function ($app) {
return new ReflectionTools();
});
Middleware for Debugging:
exportFunctionSignature() to log incoming request handling:
public function handle($request, Closure $next) {
$controller = new ReflectionClass($request->route()->getController());
$action = $request->route()->getActionMethod();
$signature = app(ReflectionTools::class)->exportFunctionSignature(
$controller->getMethod($action)
);
Log::debug("Handling: $signature", ['route' => $request->route()->getName()]);
return $next($request);
}
Dynamic Form Generation:
$tools = app(ReflectionTools::class);
$properties = $tools->getClassProperties(new ReflectionClass(\App\Models\User::class));
foreach ($properties as $property) {
$type = $property->getType()?->getName();
// Generate Blade input based on $type (e.g., text, select, etc.)
}
Event Listeners for Reflection:
$this->app->booted(function () {
$tools = app(ReflectionTools::class);
$classes = [\App\Models\User::class, \App\Services\Cache::class];
foreach ($classes as $class) {
$signatureCache = $tools->getClassMethods(new ReflectionClass($class));
// Store in cache or database
}
});
getClassMethods()/getClassProperties() in Laravel’s cache or a static variable to avoid repeated reflection overhead.ImportResolver when parsing annotations).Breaking Changes in 0.x.y:
getParameterTypes() were removed in v0.5.0—use exportFunctionSignature() instead for type extraction.0.7.*) to avoid surprises during upgrades.PHP 8.1+ Dependency:
ImportResolver Context Sensitivity:
ReflectionClass). Passing the wrong context (e.g., from a different file) will yield incorrect resolutions.Type Export Quirks:
exportFunctionSignature() may produce unexpected output for:
ArrayObject<string>) may not resolve perfectly.getParameterTypes() (if available in your locked version).Hierarchy Order:
getClassMethods()/getClassProperties() return parent methods first, which may not match expected order in some use cases (e.g., alphabetical sorting).usort($methods, fn ($a, $b) => $a->getName() <=> $b->getName());
Final Classes:
ReflectionTools and ImportResolver are final since v0.7.0—you cannot extend them.Verify Reflection Objects:
ReflectionClass is valid before passing to ReflectionTools:
$class = new ReflectionClass($className);
if (!$class->isInstantiable()) {
throw new \RuntimeException("Cannot instantiate $className");
}
Inspect Resolver Output:
ImportResolver results to debug annotation parsing:
$resolver = new ImportResolver($contextReflection);
$resolved = $resolver->resolve('Some\Class');
Log::debug("Resolved 'Some\Class' to: $resolved");
Handle Missing Types:
exportFunctionSignature() may return mixed for untyped parameters. Validate or sanitize output:
$signature = $tools->exportFunctionSignature($method);
if (str_contains($signature, 'mixed')) {
// Handle fallback or error
}
Performance Profiling:
Benchmark facade to measure overhead:
use Illuminate\Support\Facades\Benchmark;
$time = Benchmark::measure(function () {
$tools->getClassMethods(new ReflectionClass(\App\Models\User::class));
});
Log::debug("Reflection took: {$time->average()}ms");
exportFunctionSignature() behavior by wrapping it:
class CustomReflectionTools extends ReflectionTools {
public function exportCustomSignature(ReflectionMethod $method): string {
$signature = parent::exportFunctionSignature($method);
// Post-process (e.g., replace 'int' with 'integer' for consistency)
return str_replace('int', 'integer
How can I help you explore Laravel packages today?