Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Better Reflection Laravel Package

roave/better-reflection

Better Reflection is a PHP reflection API for static analysis: reflect on classes without loading them, parse classes from strings, inspect closures, and extract AST and type information. It prioritizes features over speed, so it is not intended for runtime use.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What the package actually is. Better Reflection is a source-code reflection engine: it parses PHP files with nikic/php-parser and exposes a ReflectionClass-shaped API over the AST, without loading or executing the code. It is explicitly documented as not for runtime usage — the README states performance is "much worse" than native reflection and that the "better" refers to features, not speed. That single sentence determines the entire architectural placement of this dependency. Nothing in release 6.75.0 changes this positioning.

Architecture fit. Good fit as a development and CI-time capability, poor fit anywhere else. In a Laravel codebase the realistic placements are:

Placement Fit Rationale
CI job enforcing architecture/convention rules Strong Deterministic, offline, no app boot required
php artisan diagnostics command (local/CI only) Strong Can inspect classes that would fatal on load (missing extensions, env-specific deps)
Code/scaffold generation from strings or templates Moderate StringSourceLocator is genuinely differentiating; but generation must be text/AST-based since instantiation APIs are absent
Package or library authoring (analyse own API surface) Moderate Useful for generating API docs and BC-break detection across releases
Request path, queue worker, middleware, service provider boot Unfit Order-of-magnitude slower than native reflection; adds a parser to the hot path
Replacing app()->make() / container introspection Unfit Static analysis cannot see container bindings, facade resolution, or Eloquent magic

Integration feasibility: high. MIT licensed, Composer-installable as a dev dependency, no services or infrastructure. Laravel's PSR-4 autoloading maps cleanly onto AutoloadSourceLocator, and the SourceLocator abstraction (ComposerSourceLocator, SingleFileSourceLocator, StringSourceLocator, directory/aggregate variants) makes scope explicit and reproducible — which is exactly what a CI gate needs.

Technical risk. The risk profile is dependency and drift risk, not runtime risk. 6.75.0 materially improves one line of it and leaves the rest unchanged:

  • Performance and memory. Full-tree AST analysis of a mid-sized Laravel app is seconds-to-minutes and can hold significant memory. Anything that resolves inheritance deeply (Eloquent models, trait chains) multiplies resolution work. Requires caching plus scoped directory locators.
  • php-parser version coupling. This remains the sharpest practical risk, and 6.75.0 does not address it. PHPStan, Rector, PHP-CS-Fixer, and this library all constrain nikic/php-parser. In a single composer.json, conflicting constraints are a common cause of un-installable dev dependencies. Plan for isolation (see Integration Approach).
  • PHP version ceiling — risk reduced. 6.75.0's #1593 explicitly allows installation on PHP 8.6. Previously an open question was whether supporting a future PHP minor would force a BetterReflection major. This release demonstrates the project widens the runtime constraint ahead of the PHP release within the v6 line, so PHP-version-driven major upgrades are less likely than feared. The corollary: version constraints should be ^6.75, not a narrower ~6.7x, so these widening releases are absorbed automatically.
  • API churn across majors. v6 reworked the entry point ((new BetterReflection())->reflector() returning DefaultReflector). Major upgrades track PHP releases and come with a documented UPGRADE.md — budgeted work, not free. 6.75.0 is a minor and requires no code changes.
  • Reflection-compatibility correctness — one fix relevant to rule authors. #1591 corrects ReflectionProperty::isReadable() / isWritable() for trait scope. Practical implication: any rule asserting property visibility/readability on classes that consume traits was producing wrong answers on earlier 6.x versions. If a rule of that shape exists (or is planned), re-baseline it against 6.75.0 — prior violation counts on those rules are not trustworthy. This is a silent-false-negative class of bug, which is exactly the failure mode that erodes trust in a bespoke gate.
  • Coverage gaps that matter for Laravel specifically. The compatibility matrix still shows newInstance*, ReflectionAttribute::newInstance(), getExtension(), and getClosureThis/Scope as unsupported. Consequences unchanged: attribute-driven analysis (routes, validation, authorization) yields names and argument AST only — you must interpret arguments yourself; extension-provided classes (Redis, Imagick, custom C extensions) are opaque; and global functions cannot be autoloaded, so app/helpers.php-style files and vendor files autoloads need an explicit source locator or they are invisible.
  • Static-vs-dynamic blind spot. Facades, __call/__get proxies, macros, container resolution, model attribute casting, Blade, and Livewire hydration are all invisible. A TPM must set expectations that rules can only assert on declared structure.
  • dependents: 0 is a data artifact. This library is a transitive dependency of widely used PHP tooling; real-world exposure is far higher than the metric suggests. Conversely, ~1.2k stars understates its maintenance quality — mutation-testing badge (now exercised on PHP 8.5 per #1592), type-coverage gate, docs, and an upgrade guide are strong maintenance signals.

Key questions before committing:

  1. What class of check can we not express today with Larastan/PHPStan, Rector, or a simple architecture-rule tool — and is that gap worth a bespoke tool?
  2. Does the capability run only in CI, or does it need to be reachable from an artisan command for developers? (Both are acceptable; neither touches runtime.)
  3. Which PHP versions must we support, and does that force a BetterReflection major we'd have to upgrade again within 12 months? Largely answered by 6.75.0 — PHP 8.6 is supported inside v6, and the project tracks PHP releases quickly. Residual question: pin to ^6.75 and confirm our target PHP matrix is inside the supported set.
  4. Do we need to analyse vendor/ and internal/extension classes, or only app/? (The latter removes most of the coverage gaps.)
  5. Who owns the ruleset six months from now, and what is the review process for adding a rule?
  6. Can we accept a fail-closed CI gate — i.e., a parser exception blocks the build rather than silently passing?
  7. Is a separate tooling composer.json acceptable to the team, or must everything live in the app's dev dependencies?
  8. New: which of our candidate rules depend on property visibility/readability semantics (i.e. are affected by the #1591 fix), and do we need to re-baseline any measurements taken on a pre-6.75.0 version?

Integration Approach

Stack fit. Treat this as a tooling library, not a Laravel library. Recommended comparison before adoption:

Need Existing option Where Better Reflection adds value
Type/flow analysis Larastan / PHPStan Nothing — do not duplicate; PHPStan already ships its own reflection layer
Automated refactors Rector Nothing
Architecture/dependency rules Arkitect-style tools Only if we need AST bodies or unloaded-class inspection
"Reflect on a class without loading it" none This is the unique slot — e.g. auditing classes that fatal on load, or analysing code from strings

If the answer to "what are we building?" is a convention and architecture CI gate, Better Reflection is a reasonable engine. If the answer is "better static analysis," buy a tool rather than build one.

Isolation and dependency hygiene. Install with composer require --dev roave/better-reflection:^6.75 in the application only if php-parser constraints resolve against the existing static-analysis stack. Otherwise, create a dedicated tooling project (tools/architecture/composer.json) invoked by CI, or use bamarni/composer-bin-plugin to give the tool its own namespace and lockfile. Isolating avoids the most likely failure mode: a Rector or PHP-CS-Fixer upgrade becoming blocked by, or blocking, this package. Use a caret constraint so per-minor releases like 6.75.0 (PHP 8.6 widening, bugfixes) arrive unattended; gate majors behind manual review.

Wiring in a Laravel app (if not isolated):

  • Bind the reflector as a singleton in a ToolingServiceProvider registered conditionally — $this->app->environment(['local', 'testing']) or an explicit config('tooling.enabled') flag. Never register unconditionally.
  • Configure an AggregateSourceLocator over explicit directories (app/, src/, packages/*/src) plus ComposerSourceLocator only for the vendor paths genuinely required. Prefer directory locators over AutoloadSourceLocator for CI determinism — the autoload technique assumes one class per file and a non-authoritative autoloader, both of which break under --classmap-authoritative or generated/compiled production autoloads.
  • Add a config/tooling.php with paths, exclusions, baseline/exemption list, and cache settings so rules are data, not hardcoded.
  • Expose one artisan command (php artisan arch:check) that returns a non-zero exit code on violation. This is the entire developer-facing surface.

Adapter layer. Wrap the package behind a small internal interface (DeclarationInspector or similar) exposing only what rules need — class name, methods, parameter types, return types, docblock, AST, and property visibility. Two benefits: rules never import Roave\ namespaces, so a major upgrade touches one file; and the same interface can be backed by native reflection later if the package is dropped. Because isReadable()/isWritable() semantics changed in 6.75.0, keep any visibility assertions behind this adapter rather than open-coded in rules.

Compatibility. 6.75.0 is a minor feature release with no deprecations or BC breaks recorded; no adapter or rule changes are required to move from any 6.7x version. The only behavioural delta to watch is the trait-scope property visibility fix — it is a correctness improvement, so expect it to surface previously-unreported violations if such rules exist. Treat that as a one-time re-baseline event, not a regression.

Sequencing.

  1. Spike (2–3 dev-days). Point an aggregate directory locator at app/; reflect 100–200 representative classes including Eloquent models, jobs, form requests, enums, and at least one trait-heavy class (to exercise the #1591 path). Measure wall time, peak memory, and failure count. Confirm composer constraint resolution against the existing dev stack on the newest supported PHP. Go/no-go here.
  2. Phase 1 — one rule, non-blocking. Pick a rule with zero false-positive tolerance (e.g. "every queued Job implements ShouldQueue", "no app/Models class depends on App\Http"). Report-only in CI, published to PR comments.
  3. Phase 2 — caching and breadth. Add the cache layer keyed on file path + mtime/hash, cached in the CI pipeline; expand to 5–10 rules; introduce a baseline file so legacy violations are tracked, not blocking.
  4. Phase 3 — promotion to blocking. Flip to fail-closed for rules with a stable baseline and an owner. Document the rule catalogue.
  5. Phase 4 — governance. Owner, upgrade runbook (including the minor-upgrade Dependabot path and the major-upgrade UPGRADE.md path), and a quarterly review of which rules still earn their keep.

If a spike was already run on a pre-6.75.0 version, re-run only the visibility-sensitive rules and the PHP-version matrix check — the rest of the measurement stands.

Operational Impact

Maintenance. Low steady-state cost if the adapter holds. Two recurring obligations: (a) upgrading in lockstep with new PHP minor/major releases, following UPGRADE.md; (b) reconciling php-parser constraints whenever the broader static-analysis stack moves. 6.75.0 is a good demonstration of the cadence working in our favour: PHP 8.6 support arrived as a minor, meaning (a) is mostly automated. The dangerous case remains an unpinned version floating to a major that changes Reflector construction. Pin to ^6.75 with Dependabot auto-merge for minors/patches and a manual-merge policy for majors.

Support. Open source, MIT, no SLA. Maintenance quality is above average for the ecosystem: mutation testing is actively exercised across PHP versions (#1592 now covers 8.5), there is a type-coverage gate, a documented upgrade path, and releases ship roughly monthly with small, reviewable PRs. The vendor offers commercial consulting if a blocker appears. One caveat visible in this release: all three PRs came from a single contributor. That is normal for a small maintainer group and not a red flag, but it is a bus-factor consideration — do not assume a same-week turnaround for a bespoke blocker. Practically: the support model is internal ownership. A rule nobody owns becomes a build-breaking liability within two quarters. Name an owner and a code-review path for additions.

Scaling. Cost is files × analyses, not traffic. A full scan of a 1–2k class application will dominate a small CI budget without caching; with a warm cache and directory scoping it drops to negligible for unchanged code. Design for file-by-file streaming — do not accumulate all ReflectionClass objects in a collection, since AST retention is the primary memory risk. If rule count grows, split rules into parallel CI jobs by concern rather than adding workers to one job.

Failure modes and mitigations.

Failure mode Symptom Mitigation
Unparsable file (syntax newer than bundled parser) Exception on scan Fail closed; pin library ahead of PHP upgrade (6.75.0 now covers PHP 8.6)
Class not found in locator IdentifierNotFound / silently absent Aggregate locator + explicit assertion that expected classes resolve; fail on empty result set
Authoritative/optimized autoloader Wrong or empty reflection Use directory locators, not AutoloadSourceLocator, in CI
Global/vendor functions (helpers.php) invisible Rules quietly skip Explicit SingleFileSourceLocator/directory locator for those files
Stale cache False negatives after refactor Key on content hash; invalidate on branch change
Rule crashes Rule reports zero violations Fail closed; assert expected class count per run
php-parser constraint conflict composer update blocked Isolated tooling project or bin-plugin
Memory exhaustion on deep inheritance OOM kill in CI Streaming iteration, memory_limit headroom, chunked runs
Attribute arguments uninterpretable Rules can't read #[Route] semantics Parse argument AST directly; document the limitation to rule authors
Property visibility misreported in trait scope Violations missing or spurious pre-6.75.0 Upgrade to ^6.75, re-baseline visibility rules once, keep assertions behind the adapter

Ramp-up. A mid-level PHP engineer can use reflectClass() / getMethods() / getReturnType() productively in 2–3 days. Writing trustworthy rules is the real cost: handling traits, interface inheritance, promoted constructor properties, enums, docblock generics, and conditional declarations is where the 3–6 weeks go for a first meaningful ruleset. The trait-scope visibility fix in 6.75.0 is a reminder that this ramp-up includes validating the reflection layer's own edge cases, not just writing rule logic. Budget developer time, not licence cost (which is zero). Include a short internal write-up of the adapter API and the false-positive policy; the most common failure of bespoke architecture tooling is abandonment after the first wave of noisy violations.

Verdict. Approve as a scoped, dev-and-CI-only tooling dependency, unchanged from the prior assessment and now slightly de-risked: 6.75.0 raises the PHP ceiling inside v6 and fixes a correctness bug that would otherwise have silently undermined visibility rules. Adopt at ^6.75. Reject outright if the intended use is runtime reflection, container introspection, or duplicating capabilities already provided by Larastan/PHPStan. The package is well maintained and technically strong at exactly one thing — reflecting on code that is not loaded — and the value case must be anchored to that capability.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style