- How do I install spatie/invade in a Laravel application?
- Installation is a single Composer command: composer require spatie/invade. There is no service provider to register, no config file to publish, and no bootstrapping step, because the package exposes one global invade() function. The same command works identically in a Laravel app, a plain PHP project, or a legacy service, since the package has no Laravel coupling at all.
- How do I read a private property using the invade() function?
- Pass the object to invade() and access the property as if it were public, for example invade($myClass)->privateProperty. The proxy returns the actual private value regardless of visibility. This avoids writing ReflectionProperty boilerplate just to inspect a value in a test or debug script.
- Can I write or change a private property with invade()?
- Yes, the returned proxy supports assignment, so invade($myClass)->privateProperty = 'changed value' updates the private property in place. Reading it back through invade() returns the new value. Be aware that writing to a property that does not exist creates a dynamic property, which is deprecated in PHP 8.2 and an error for most classes in PHP 8.3 and above.
- How do I call a private method or a private static method with invade()?
- For instance methods, call them directly on the proxy: invade($myClass)->privateMethod(). For static members, pass the class name string and use the explicit accessors: invade(MyClass::class)->get('prop'), invade(MyClass::class)->set('prop', $value), and invade(MyClass::class)->method('privateStaticMethod')->call(). That small API surface covers private instance and static properties and methods in one documented paragraph.
- Which Laravel and PHP versions does spatie/invade support?
- The package has no Laravel dependency at all, so it works with any Laravel version as long as your PHP version satisfies the Composer constraint. Version 2.x targets modern PHP 8 releases and uses Closure::bind instead of reflection. Check the composer.json constraint before pinning, and note that the package offers no guard rails for PHP language edge cases such as readonly or uninitialised typed properties.
- Why does invade() no longer reach parent class private properties in version 2.0?
- Since 2.0.0 the implementation uses Closure::bind rather than reflection, and the closure binds to the runtime class of the object you pass, not to the declaring class. Private members declared on a parent class are therefore generally not reachable through a child instance, and behaviour can differ subtly from the reflection-based 1.x releases. Do not assume behavioural equivalence when upgrading across the 2.0 boundary, and plan a workaround or accept the limitation.
- What happens with readonly properties, uninitialised typed properties, or dynamic properties?
- The package does not intercept these cases, so failures surface as raw PHP errors. Reading an uninitialised typed property throws an Error, readonly properties introduced in PHP 8.1 cannot be written at all, and writing a non-existent property creates a dynamic property that is deprecated in PHP 8.2 and an error for most classes in PHP 8.3 and above. Private constants are out of scope entirely.
- Does spatie/invade ship a PHPStan extension and should I install it?
- Yes, the package ships a PHPStan extension that silences access to private property and method diagnostics. That is convenient, but it also disables exactly the signal that would otherwise flag misuse of invade() in your codebase. If you adopt the extension, adopt it consciously and pair it with a team rule about where invade() is allowed.
- Should I use invade() in production code paths?
- Treat invade() as a deliberate escape hatch: allow it in tests, debugging, and one-off tooling, and keep it out of application source unless a named legacy subsystem justifies the dependency. Encapsulation erosion is the dominant risk, and it is a people risk rather than a code risk, because the barrier to reaching into private state drops to near zero once the function is in composer.json. Note also that the function is declared behind a function_exists guard, so a namespace collision degrades to a silent no-op rather than a fatal error.
- What are the alternatives to spatie/invade?
- The native options are the Reflection API or a hand-written Closure::bind call, both of which involve more boilerplate but no extra dependency. The better long-term alternative is usually to add a public API, a testing seam, or a dedicated test double so private state never needs reaching into. Reach for invade() when you want the shortest possible path in a test and are comfortable with a small, Spatie-maintained, zero-dependency utility.