suin/php-expose
Expose helps PHPUnit tests access private/protected properties and methods in PHP objects. Use a simple fluent API to set non-public attributes and call hidden methods without changing production code. Requires PHP 5.3+ and installs via Composer.
Installation:
composer require suin/php-expose
Ensure vendor/autoload.php is included in your PHPUnit bootstrap file (e.g., phpunit.xml):
<php>
<autoload>
<file>vendor/autoload.php</file>
</autoload>
</php>
First Use Case: Test a class with private/protected properties/methods. For example:
use Expose\Expose as e;
class User {
private $name;
protected $email;
private function validateEmail() { ... }
}
$user = new User();
$user->name = 'John'; // Normally inaccessible
// Expose and test
e::expose($user)
->attr('name', 'John') // Set private property
->attr('email', '[email protected]'); // Set protected property
$result = e::expose($user)->call('validateEmail'); // Call private method
Property Access:
Expose::expose($object)->attr('property', $value) to set/get private/protected properties.e::expose($user)
->attr('name', 'Alice')
->attr('email', '[email protected]');
Method Invocation:
Expose::expose($object)->call('method', $arg1, $arg2).$validationResult = e::expose($user)->call('validateEmail');
Test Setup:
setUp() to initialize test objects with default values:
public function setUp(): void {
$this->user = new User();
e::expose($this->user)
->attr('name', 'Test User')
->attr('email', '[email protected]');
}
Assertions:
$this->assertEquals('Test User', e::expose($this->user)->attr('name'));
$mock = $this->getMockBuilder(User::class)->disableOriginalConstructor()->getMock();
e::expose($mock)->call('privateMethod');
Expose functionality in custom traits for project-specific needs.Visibility Leakage:
Static Methods:
Expose does not handle static private/protected members. Use reflection directly if needed:
$reflection = new ReflectionClass(User::class);
$method = $reflection->getMethod('_staticMethod');
$method->setAccessible(true);
$method->invoke(null, $arg);
Performance:
PHPUnit Integration:
Expose is loaded before tests run (via bootstrap.php in phpunit.xml). Failure to do so may result in undefined class errors.Undefined Property/Method:
Expose::expose($object)->attr('property') or ->call('method').Reflection Errors:
Expose fails silently, enable PHP error reporting or wrap calls in try-catch:
try {
e::expose($user)->call('nonexistentMethod');
} catch (\Exception $e) {
$this->fail("Method call failed: " . $e->getMessage());
}
Selective Exposure:
Expose only for properties/methods that are genuinely needed in tests. Document why each is exposed.Custom Expose Classes:
Expose for project-specific needs:
class CustomExpose extends \Expose\Expose {
public function exposeStatic($class, $method, ...$args) {
$reflection = new ReflectionMethod($class, $method);
$reflection->setAccessible(true);
return $reflection->invokeArgs(null, $args);
}
}
IDE Support:
Expose calls. Use @method or @property annotations in test classes to improve autocompletion:
/**
* @property string $name
* @method string _privateMethod()
*/
class UserTest extends \PHPUnit\Framework\TestCase { ... }
Alternatives:
ReflectionClass: More control but verbose.Artisan::call(): For testing commands with private methods (if using Laravel).How can I help you explore Laravel packages today?