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

Property Access Laravel Package

symfony/property-access

Symfony PropertyAccess reads and writes object and array values using simple string notation such as "user.address.street". It handles getters, setters, public properties, ArrayAccess, and magic methods, making nested data access generic and consistent across forms, serializers, and valida...

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a framework-agnostic Symfony utility component, not a Laravel package: no service provider, no facade, no config file, no publishable assets. It is a leaf dependency that does one thing — resolve a string path (user.address.city, items[0].sku, order.lines[2].qty) against an object/array graph for reads and writes, across getters, issers/hassers, public properties, __get/__set, ArrayAccess, and array keys. The gap it fills in Laravel is real but narrower than it first appears: data_get already handles arrays, ArrayAccess, and public object properties, but it does not traverse getters, magic accessors, or private typed state — which is exactly the shape of DTOs, value objects, read models, and third-party SDK payloads. Best-fit consumers: HTTP request → DTO mapping, webhook/payload normalization, view/read models, config and feature-flag path resolution, test fixture builders. Poor-fit consumers: Eloquent models (already ArrayAccess with their own accessor/mutator semantics — layering this on top creates a second, competing access model) and flat arrays (native helpers win on clarity and speed).

Integration feasibility. High. One composer require, one singleton binding, one thin adapter with two methods. The public surface is small and stable in shape: PropertyAccessor (getValue/setValue/isReadable/isWritable, plus the invalid-index and magic-call toggles), PropertyPath for parse/validate, and the exception types (NoSuchPropertyException, NoSuchIndexException, UnexpectedTypeException). Note the distribution model: this repo is a read-only split of the Symfony monorepo — issues, PRs, and security advisories are handled in symfony/symfony, so GitHub stars and "dependents" here say nothing about adoption or health. Verify the transitive delta against the lockfile before committing; the component sits inside the Symfony component graph and can pull siblings you do not otherwise carry.

Technical risk. Four items to fund mitigations for:

  1. Resolution order is still being patched. Recent fixes landed simultaneously across 6.4/7.x/8.x: public properties shadowed by same-named methods requiring arguments, getter-shaped methods returning void/never, PHP 8.4 asymmetric visibility, cached null read info. The answer to "what does getValue($o, 'foo') actually call?" can therefore change in an x.y.Z bump. Treat resolution order as an assumption to assert in tests, not documented behavior to rely on. Pin exact versions; review patch bumps.
  2. Version floor is coupled to PHP. Symfony 8 requires PHP ≥ 8.4. An innocent ^6.4|^7.0|^8.0 constraint silently raises the runtime floor for the whole app. Constrain to the branch matching our PHP/Laravel matrix.
  3. User-controlled path strings become an object-graph read/write primitive. getValue($user, $userSuppliedPath) is a data-exfiltration path (hidden attributes, tokens, relation traversal) and setValue is mass assignment. If paths can ever originate from saved filters, admin rules, or request payloads, this needs an allow-list before it ships.
  4. Exceptions are the normal miss signal. user.profile.avatar throws when profile is null. Any adapter that does not convert misses into a domain-level default will produce 500s on ordinary sparse payloads.

Also worth naming: on Eloquent, __get is the attribute accessor, so getValue($model, 'remember_token') returns the token regardless of $hidden (which only affects serialization) — and traversing customer.address.city silently triggers lazy loads.

Key questions.

  • Which concrete consumer ships this quarter — DTO mapping, config paths, or something else?
  • Will path strings ever be user-supplied? If yes, whose allow-list and where is it enforced?
  • What is our PHP/Laravel floor, and which Symfony branch does that map to?
  • What does this replace: bespoke reflection helpers, a data_get-plus-getter-chain hybrid, or nothing?
  • Who owns the adapter and the path-convention doc — a named team, not "platform"?
  • Is the transitive dependency delta acceptable?

Integration Approach

Stack fit. Install as a leaf dependency, bind PropertyAccessor as a container singleton (it caches reflection read/write metadata per instance — constructing one per call throws that away), and expose it only through an internal interface, e.g. Reader::read(object|array $target, string $path, mixed $default = null): mixed and a separate Writer. Keep Symfony types out of application-level signatures (controllers, jobs, Blade) so the component is swappable in one file if it disappoints. Do not use it on Eloquent models; if a path must cross a model, resolve the model's data through its own API first.

Migration path. Inventory existing traversal code before adopting — grep for chained getters, property_exists, ReflectionProperty, ReflectionClass, and data_get calls on objects — so you are sizing a cleanup, not just an install. Then pick exactly one bounded context, almost always read-only DTO/resource mapping: request-scoped, easy to test, easy to feature-flag, no write-side blast radius. Migrate that consumer, delete the bespoke code it replaced, and only then consider writes.

Compatibility. Constrain to the Symfony branch matching our PHP floor (6.4 and 7.4 are LTS branches; 8.x is standard-track and PHP 8.4+). Confirm the component's PHP requirement against the Laravel version matrix we actually run. Because Laravel does not test against this component, we own the integration contract: add a small compatibility test that exercises read, write, miss, and a magic-accessor case against the pinned version, and run it on every dependency bump.

Sequencing.

  1. Spike (≤2 days): adapter interface, PropertyAccessor singleton, exception→default translation, path convention doc.
  2. Feature-flag one read-only consumer; ship with the flag off; enable in one environment.
  3. Add a path allow-list mechanism as a reusable guard (enum/constant-backed paths, or a regex allow-list) — before any write support exists.
  4. Enable writes only for DTOs with validation upstream, and only with allow-listed paths.
  5. Broaden after two clean release cycles; retire the replaced helpers and update the internal docs in the same PR.

Operational Impact

Maintenance. Low code volume, non-trivial discipline. The component itself is stable; the churn is in patch releases that adjust resolution order, so upgrades should be treated as behavior changes: pin, read the changelog, run the compatibility test, then bump. Security advisories arrive through Symfony's process (and composer audit), not this repo — ensure the pipeline watches the monorepo advisory feed. Ownership is internal: assign a named team for the adapter, the path convention doc, and the allow-list. Symfony community support is best-effort via the monorepo issue tracker; no vendor SLA at this tier.

Support. Expect two classes of internal tickets: "path returns null / throws unexpectedly" (usually sparse data or an accessor-name mismatch) and "why didn't my setter/mutator run" (write path bypassing domain logic). Both are prevented by documentation and by the adapter's default-on-miss behavior, not by runtime fixes. Budget ~1 day of onboarding per engineer touching it; the component is small, the semantics are the hard part.

Scaling. CPU cost is negligible. The real scaling risks are graph-shaped:

  • Hidden I/O. A nested path over ORM entities triggers lazy loads per segment — a getValue($order, 'customer.address.city') in a loop is an N+1 generator. Lint paths used in loops; require eager loading where paths cross relations.
  • Reflection cache growth. Per-instance metadata caches grow with the number of distinct classes touched; a singleton per worker keeps this bounded and reused.
  • No compile-time safety. Path typos fail at runtime, in whichever request hits them. Mitigate with path constants/enums and a test that asserts every declared path is isReadable() on its target.

Failure modes.

  • Sparse payload → NoSuchPropertyException → unhandled 500. Mitigation: adapter returns the supplied default and logs at debug.
  • Wrong resolution target after a patch bump (property vs. method) → silently different values. Mitigation: pinning plus assertions in tests.
  • Magic-method side effects (__get doing work, proxies initializing, lazy loads firing) → surprising queries or side effects on read.
  • enableMagicCall() combined with user-influenced paths → arbitrary method invocation on our objects. Keep magic call disabled by default and treat enabling it as a reviewed decision.
  • Writes bypassing validation, mutators, model events, and authorization → mass assignment into fields we never intended. Mitigation: allow-listed paths, DTO-only targets, validation upstream.
  • Sensitive-data reads (password_hash, remember_token, hidden attributes) via path strings — $hidden does not protect direct access, so treat any non-literal path as untrusted input.

Ramp-up. Small engineering lift, modest process lift. One engineer can deliver the adapter plus the first consumer in a week; the ongoing cost is the review discipline — pinned versions, allow-listed paths, no Eloquent targets, adapter-only access. Recommendation: proceed on that scope, and treat write support and user-supplied paths as separate, later decisions with their own review.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib