jetbrains/phpstorm-attributes
JetBrains PhpStorm attributes provide PHP 8 attributes for richer code completion and analysis in PhpStorm, including Deprecated, ArrayShape, ObjectShape, and Immutable. Bundled with PhpStorm; add as a dev dependency for other static analysis tools.
Architecture fit. jetbrains/phpstorm-attributes is not an architectural component — it is a metadata vocabulary. The package is a small set of #[\Attribute] classes (Deprecated, ArrayShape, ObjectShape, Immutable, Pure, ExpectedValues, NoReturn, Language, FileReference) under the JetBrains\PhpStorm namespace. PHP attributes are inert unless reflected upon, so these classes introduce no runtime coupling, no service provider, no config, no middleware, no lifecycle hooks. They can be applied at any layer of a Laravel application (controllers, JsonResource::toArray(), config providers, DTOs, service boundaries) without touching dependency wiring. Critically, it is a dev-only dependency, so production autoloading and container boot are unchanged. The correct mental model is "structured comments the IDE and analysers can read."
Integration feasibility. Extremely high. One composer require --dev jetbrains/phpstorm-attributes line, no publish step, no environment variables, no bootstrap. The attributes ship bundled with PhpStorm 2020.3+, so the composer install only exists to satisfy CI tools and non-PhpStorm analysers that would otherwise raise "class not found." There is no migration, no schema change, and no data surface. Reversibility is essentially total: composer remove plus leaving the attribute lines in place (harmless) or stripping them.
Technical risk.
#[ArrayShape] against the actual array a method returns. A stale shape annotation is worse than no annotation because both the IDE and reviewers trust it. Mitigation: pair with phpstan-phpstorm + PHPStan in CI, or explicitly accept IDE-only, best-effort semantics.#[Immutable] does not prevent mutation; #[Pure] does not prevent side effects; #[NoReturn] does not guarantee termination. Teams that mistake these for enforcement may deprioritise readonly properties or real encapsulation.composer install --no-dev, any first-party code calling ReflectionClass::getAttributes() on these namespaces would hit class-not-found. Laravel core does not do this; custom code might.Key questions a TPM must answer before green-lighting.
phpstan-phpstorm plus a PHPStan level that actually parses attributes — or are we buying IDE sugar only? This decision determines whether drift is caught or merely discovered.#[Deprecated] plug into an existing deprecation policy with named owners and semver alignment, or are we inventing one retroactively?Stack fit. The package is a natural fit for a Laravel application on PHP 8.1+ with Composer, because Laravel's idioms are exactly the shapes these attributes describe: untyped structured arrays from config(), JsonResource::toArray() payloads, data_get() consumers, form-request rule arrays, and json_decode(..., false) / DB::select() rows that arrive as mixed. #[ArrayShape] gives Laravel the closest thing it has to typed config() without a DTO rewrite; #[ObjectShape] covers dynamic object rows. There is no Laravel service provider to register and no config/phpstorm-attributes.php to publish — which is the point.
Compatibility.
^1.3. The release cadence is slow, which is appropriate for a metadata-only package; slow cadence here is a feature, not a warning sign.--no-dev/--dev split is the one compatibility line to state explicitly in the convention doc: production installs lack the classes, so no runtime code may reflect over them.Migration path. There is no data or runtime migration — only a convention migration. Sequence it as follows:
#[Deprecated] and #[ArrayShape] in a scratch file, and confirm CI is byte-for-byte unaffected.JsonResource::toArray() annotated with #[ArrayShape], and one method on the internal deprecation backlog annotated with #[Deprecated(reason:…, replacement:…) where replacement uses %class% / %method% / %parameter0% substitution so the quick-fix rewrites call sites.phpstan-phpstorm and enable attribute parsing for the pilot paths only, keeping the existing baseline intact so the change is a delta, not a cliff.CHANGELOG deprecation entry ships in the same PR as its #[Deprecated] attribute. This is where the package pays for itself: it converts a changelog line into an inline codemod during Laravel major-version upgrades and internal package revamps.Sequencing against adjacent work. Land this before or alongside a Laravel major upgrade, not after — the quick-fix value is realised during the migration window while the deprecated call sites are still live. Remove attributes only when the underlying symbols are deleted.
Maintenance. Near zero. No runtime process, no scheduled job, no infrastructure, no database, no configuration to drift. Ongoing cost is a pinned version plus automated dependency bumps (Dependabot/Renovate). The abandonment scenario is largely pre-absorbed by JetBrains shipping the attribute definitions inside the IDE itself; worst case, a CI-only stub file restores class resolution.
Support. The support surface is developer education, not operations — the tickets read "is this annotation correct?" rather than "why is production down." Owner should be a tech lead or DevEx function, not platform/SRE. Budget for one annotated example PR and one short walkthrough; there is no certification, no vendor contract, and no escalation path beyond the PhpStorm issue tracker.
Scaling. Annotation cost scales with the number of public boundaries, not with traffic. Request latency, memory, and production autoload are unaffected — zero runtime footprint means zero production scaling concern. In a monorepo or multi-team codebase, the scaling risk is inconsistency: three teams annotating three different ways, or one team annotating exhaustively while another ignores it. Mitigate with the convention doc plus CI enforcement; without CI, scale amplifies drift.
Failure modes to monitor.
#[ArrayShape] doesn't move, and the IDE now lies. Caught only by static analysis in CI; if CI is out of scope, treat this as an accepted, documented risk.--no-dev. Add a lint rule or a note in the convention.^1.3 and verify new attribute names against the tag before use.#[Immutable] as enforcement and skipping readonly or real encapsulation. State plainly in the convention: these are advisory.Ramp-up. Fifteen to thirty minutes per engineer for the two attributes that matter (#[Deprecated], #[ArrayShape]); #[ObjectShape], ExpectedValues with backed enums, and #[NoReturn] are learned on demand. A single brown-bag session and one merged exemplar PR is sufficient onboarding. Net effect on new hires is positive: key-level autocomplete on resource payloads and config arrays shortens the "what does this endpoint actually return?" phase that normally consumes their first weeks.
Adoption metrics worth reporting upward. Count of annotated public boundaries; percentage of shipped deprecations carrying a replacement hint (codemod coverage); PHPStan baseline size as an inverse quality signal; review comments referencing payload shape or deprecation, trending toward zero. Framed for executives: a one-line, zero-runtime, Apache-2.0 dev dependency that reduces avoidable defects and turns framework upgrades into guided quick-fixes — a low-cost enabler to fold into existing feature work rather than a standalone roadmap item.
How can I help you explore Laravel packages today?