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

Strict Objects Laravel Package

kdyby/strict-objects

kdyby/strict-objects provides a PHP trait that makes objects strict: reading or writing undeclared properties throws an exception instead of silently creating dynamic ones. It catches typos and enforces explicit property declarations in Nette projects.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

kdyby/strict-objects is a single-responsibility behaviour trait. It supplies magic member-access hooks (__get, __set, __isset, __unset, __call, __callStatic) that raise a MemberAccessException when a class is asked for a member it does not declare, replacing PHP's default silent null or unconditional method dispatch. There is no container binding, no service provider, no configuration, and no framework coupling — the architectural question is therefore not "does it fit Laravel" but "does it fit the object model of the classes we put it on."

Good candidates: hand-written domain value objects (Money, Email, DateRange), non-Eloquent DTOs, command/query handlers, small result objects, and any class whose public surface is fully declared and constructor-injected.

Poor candidates, and these dominate a typical Laravel codebase: anything inheriting magic-member behaviour from a parent or another trait — Eloquent models (whose __get/__set/__isset back attributes, casts and relations), Illuminate\Support\Fluent, Macroable consumers (whose __call drives macro dispatch), Doctrine entities with proxy hydration, container property-injection targets, and Blade-facing view models that lean on null-coalescing. In these cases the trait does not fail loudly; it silently displaces the framework's hook and breaks the class in ways that surface far from the change.

Integration feasibility

Installation is trivial (composer require, then one use statement per class). Integration effort is measured in developer-minutes per class, not tickets. The real cost is the per-class validation pass: identifying every class where introducing the trait changes behaviour under existing magic-method dispatch. That audit, not the install, is the work.

Technical risk

  • Abandonment. Last release 2020-12-17, 31 stars, 0 dependents, single maintainer. No security policy, no visible modern-PHP CI, no evidence of PHP 8.2+ dynamic-property work.
  • License ambiguity (blocking). Metadata reports NOASSERTION. Until a human reads the license file, this cannot clear a standard OSS intake review. Kdyby's historical packages were permissive, but "probably fine" is not an approval.
  • Composer constraint risk. A 2020 composer.json may cap php below your runtime or pin dev dependencies that conflict in a Laravel 10/11 graph. The trait body itself is version-agnostic plain PHP, so this is a packaging problem — but it can block composer update, and --ignore-platform-reqs is not an acceptable workaround.
  • Static-analysis regression (most important). Declaring __get/__set marks a class as "magic" to PHPStan and Psalm, which then stop reporting genuinely undefined properties on it. The package trades a build-time error for a runtime error on precisely the classes where you most want static guarantees. In a codebase already running PHPStan at level 6+, this is likely net-negative.
  • Semantic contract violations. Code in the wild probes with isset($obj->prop) and expects a boolean; serializers and hydrators introspect via get_object_vars and reflection; json_encode bypasses __get entirely. A "strict" object can therefore serialize successfully without the member that reading it throws on.
  • Contained blast radius. The trait is tens of lines, readable in a code review, and trivially forkable — which is also the strongest mitigation available.

Key questions

  1. What is the actual license? (blocking)
  2. Does it install on PHP 8.2/8.3/8.4 and Laravel 10/11 without --ignore-platform-reqs? (blocking)
  3. Which exact classes would adopt it, and does any of them inherit __get/__set/__call from a parent or another trait? Trait methods win over inherited ones silently — verify per class.
  4. What is the trait's isset/empty semantics — throw, or return false? This single answer determines whether probing code breaks.
  5. What does our current PHPStan/Psalm level already catch that this would only catch at runtime? High overlap means near-zero ROI.
  6. Could any target class instead be final with declared, typed properties? Declaration is cheaper and safer than a runtime guard.

Integration Approach

Stack fit

Laravel-side, treat this as a per-class opt-in utility, never a base-class concern. It must not be applied to Illuminate\Database\Eloquent\Model subclasses or to Macroable consumers, where its hooks collide with framework dispatch. Safe first targets are domain value objects, non-Eloquent DTOs, and constructor-only domain services.

Migration path

  1. Do not install first. Raise PHPStan/Psalm to a level that reports undefined property and method access, and fix what it finds. That captures the majority of the value at build time with zero runtime risk.
  2. If runtime strictness is still required — for example in a published SDK where you cannot control the caller's analysis — do not depend on an unmaintained package to provide it. Copy the trait (small), retain the license header per its terms, re-namespace it, add tests, and own the release.
  3. Adopt the package itself only if the license clears and a named maintainer accepts vendor-level ownership — and then only in the class categories above.

Compatibility

  • Code level: plain PHP, runs on 8.x.
  • Composer level: verify require constraints against the real lockfile.
  • Framework level: incompatible with Eloquent, Fluent, and any class whose parent defines the same magic methods.
  • Tooling level: expect new "class has magic __get" behaviour in PHPStan/Psalm that suppresses diagnostics; baseline or annotate deliberately.

Sequencing

  1. License review (legal, blocking).
  2. Compatibility spike in a scratch branch, including composer update --dry-run against the real lockfile.
  3. Pilot on one bounded domain module — 5–10 classes maximum — behind the existing test suite.
  4. Measure: did the suite catch anything static analysis missed? Did anything in the module fail with MemberAccessException during serialization, queueing or view rendering?
  5. Go/no-go, then widen module by module. Never retrofit framework base classes.

Operational Impact

Maintenance

Ownership sits with the adopting team regardless of who publishes the package. With no release since 2020, expect to patch constraints — and eventually the code — yourself. Budget it as "vendored code with a public upstream," not as "a dependency." Upgrade risk is near zero because the API surface is tiny, but so is upstream responsiveness: a future PHP change to magic-method semantics lands on you.

Support

No SLA, no security disclosure policy, one maintainer, 31 stars, 0 dependents. The escalation path is a GitHub issue that may never be read. The internal runbook must be "we can patch this in an hour because it is forty lines we have read."

Scaling

Runtime overhead is negligible: the hooks fire only on access that would otherwise be an error, and backtrace construction in the exception is paid on bugs, not on the hot path. The scaling concern is cognitive — every new engineer must know which classes are "strict" and why isset behaves unexpectedly there. Enforce that with a namespace convention or a lint rule, not tribal knowledge.

Failure modes

  • Production 500 where a silent null used to be. A nullable read in a Blade partial, a queued job hydrating a payload, or a third-party package probing isset($obj->foo). Turning silent-null bugs loud is the point — but loud in production is worse than loud in CI. Confirm error handling reports class and member context independent of APP_DEBUG.
  • Serialization drift. json_encode, toArray and reflection hydrators bypass __get, so a strict object can serialize successfully without the member that reading it throws on. Test the round-trip explicitly.
  • Silent no-op integration. Applying the trait where a parent already defines __get can produce no error, no warning and no behaviour change. Per-class verification is mandatory.
  • Tooling blind spot. Static analysis stops flagging undefined properties on strict classes, converting a CI failure into a runtime failure.

Ramp-up

Minutes to use; roughly the length of a magic-method audit to use safely — a day for a small app, a week for a large legacy one. Given a score of 0.485 and an opportunity score of 34.31, the defensible recommendation is to document the pattern internally, prefer declared typed properties plus static analysis, and treat this repository as a reference implementation to copy from rather than a dependency to adopt.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core