spatie/invade
Access and manipulate private/protected object and static properties, and call private methods in PHP using a simple invade() helper. Handy for testing, debugging, and working around encapsulation when needed, without verbose reflection code.
Install with composer require spatie/invade. The package exposes a single global function invade() that bypasses PHP’s visibility modifiers—enabling read/write access to private properties and invocation of private methods on objects or classes.
First use case: Write a unit test for legacy code where internal state isn’t exposed publicly. Use invade($service)->currentState to assert the internal state without modifying the class.
use function Spatie\Invade\invade;
// Object instance: read/write properties
$object = new MyClass();
$state = invade($object)->privateProperty;
invade($object)->privateProperty = 'new value';
// Static context: use `get()`, `set()`, and `method()`
$staticValue = invade(MyClass::class)->get('staticProp');
invade(MyClass::class)
->method('privateStaticMethod')
->call('arg1', 123);
Start by checking tests/ for patterns—most usage lives in testing, debugging, or integration workarounds.
$sut = new ComplexService();
invade($sut)->validationErrors = ['error' => 'test'];
$this->assertTrue(invade($sut)->hasErrors());
invade in production business logic.invade sparingly and encapsulate it behind a test-only helper.phpstan.neon:
includes:
- vendor/spatie/invade/extension.php
This suppresses false positives when accessing invade()’s returned proxy.invade(MyClass::class)), not an object instance. Static access uses get()/set()/method() APIs—direct property access fails silently on static members.invade() uses ReflectionProperty::setValue() and Closure::bind() without instantiating Reflection classes manually—more reliable but still incurs overhead. Avoid in hot paths.invade($obj)->privateProp—always cast or document the expected type. Consider:
/** @var string $value */
$value = invade($obj)->privateProp;
invade calls in test helpers (e.g., PrivateAccessor::get($obj, 'prop')) to centralize future refactoring and avoid scattering low-level calls.invade() does not sanitize inputs. Avoid passing untrusted strings to property/method names (e.g., invade($obj)->{$_GET['prop']}).invade(MyClass::class)->get('prop') only works for existing static properties. Private static methods require method('methodName')—no magic __get/__call support.How can I help you explore Laravel packages today?