nette/reflection
Standalone PHP reflection library from Nette that extends PHP’s Reflection API with easier inspection of classes, methods, properties, parameters, and annotations. Useful for frameworks, DI containers, and code analysis tools.
Treat this as a legacy dependency, not a greenfield choice: the package is archived, its last release was 2017, and the description literally begins with [DISCONTINUED]. It exists because Nette Framework 2.x and a long tail of packages used it for docblock annotations and convenience reflection wrappers.
Minimal steps:
NOASSERTION, which usually means the metadata is missing; Nette packages of that era are BSD-3-Clause, but verify the LICENSE file in the repo/fork you actually install.composer require nette/reflection:2.4.*
Composer will print an "abandoned" warning; that's expected and does not block installation.src/Reflection/ClassReflection.php — the wrapper, from() factory, getAnnotations() / hasAnnotation() / getAnnotation().src/Reflection/AnnotationsParser.php — the actual docblock parser.src/Reflection/Annotation.php — what you get back (name plus parsed value arguments).
The whole package is a handful of files; there is no separate manual.$ref = Nette\Reflection\ClassReflection::from(MyService::class);
if ($ref->hasAnnotation('inject')) {
// ...
}
foreach ($ref->getAnnotations() as $annotation) {
echo $annotation->name; // 'route'
var_dump($annotation->value); // ['/home', 'method' => 'GET']
}
ReflectionClass plus PHP 8 attributes; for docblock parsing only, use phpdocumentor/reflection-docblock. Reach for nette/reflection when you are maintaining a Nette 2.x-era codebase and want the smallest diff.Annotation-driven registration (the classic pattern). Scan a directory of classes, reflect each with ClassReflection::from(), and build a map from annotations such as @inject, @service, @route, or @persistent. Always go through from() so you can accept a string, an object, or an existing ReflectionClass interchangeably.
Custom annotation arguments. The parser handles both positional and named arguments:
/**
* @route("/users", method="GET", priority=10)
*/
Use named arguments in your own annotations. They survive argument reordering and make the parsed value array self-documenting.
Property and method metadata. PropertyReflection::getAnnotation('var') and MethodReflection::getAnnotation('return') / getAnnotation('param') were the standard way to recover types before typed properties and return types. If you're on PHP 7.4+, prefer native types and reserve annotations for things native reflection cannot express (routes, DI hints, serialization rules).
Standalone docblock parsing. AnnotationsParser::parsePhpDoc($docblock) is useful when you have a doc comment string but no reflection object — e.g. parsing annotations out of generated code or a template file.
Cache behind your own layer. Reflection plus docblock parsing is cheap per class but adds up when you scan hundreds of classes on every request. Memoize a [class => metadata] array in-process; for cross-request caching, serialize to APCu or a file cache keyed on filemtime.
Adapter/wrapper pattern — the most important tip. Wrap the package behind your own small MetadataReader interface (getClassAnnotations(string $class): array, etc.). Callers depend on your interface, not on Nette\Reflection\*. This is what makes the eventual migration to attributes or roave/better-reflection a one-file change instead of a codebase-wide grep.
Coexistence with native reflection. ClassReflection extends \ReflectionClass, so instanceof \ReflectionClass still holds and you can pass it anywhere native reflection is accepted. Use ClassReflection only where you need annotations, and native reflection everywhere else.
ReflectionParameter::getClass() was deprecated and internal method signatures changed; overridden reflection methods in an archived package can fatal or silently misbehave. Test on the exact PHP version you deploy before committing to it.vcs repository.@Foo class names from plugin or user-supplied code, you create an instantiation/injection vector. Whitelist the annotation names you accept.filemtime and disable caching in dev.@Inject vs @inject); matching behavior is not something to rely on across versions.NOASSERTION will trip automated license scanners. Resolve it explicitly in your dependency review rather than inheriting the ambiguity.phpdocumentor/reflection-docblock for docblock parsing, or symfony/property-info / roave/better-reflection for richer type resolution.AnnotationsParser is the only meaningful seam — subclassing the reflection wrappers is possible but fragile on an unmaintained base. Prefer composition around the parser over inheritance from the reflection classes.How can I help you explore Laravel packages today?