Installation:
composer require skagarwal/reflection --dev
Usage in Tests:
ReflectableTrait in your test class (e.g., PHPUnit test case):
use SKAgarwal\Reflection\ReflectableTrait;
class UserTest extends \Tests\TestCase
{
use ReflectableTrait;
}
First Use Case:
setUp() (for PHPUnit):
protected function setUp(): void
{
$this->user = new \App\Models\User();
$this->reflect($this->user);
}
public function testPrivateMethod()
{
$result = $this->call('validateName', ['John Doe']);
$this->assertTrue($result);
}
Reflecting a Single Class:
reflect() in setUp() for persistent reflection:
$this->reflect(new \App\Services\PaymentService());
on() for one-off calls:
$this->on(new \App\Services\PaymentService())
->call('processPayment', [$amount])
->get('transactionId');
Testing Private/Protected Methods:
$this->call('calculateTax', [$subtotal]);
$this->on($order)->callProcess()->get('total');
Property Manipulation:
$this->set('isActive', true);
$this->get('userId');
$this->getUserId; // Equivalent to $this->get('userId')
Multiple Classes in One Test:
setUp():
$this->reflect($user = new \App\Models\User());
$this->reflect($order = new \App\Models\Order());
reflect() and on():
$this->on(new \App\Services\Logger())->call('logError', [$message]);
Illuminate internals (e.g., Illuminate\Foundation\Application boot methods):
$this->reflect(app());
$this->call('bootProviders');
Illuminate\Container\Container):
$this->reflect(app)->call('make', ['App\Contracts\Service']);
handle() methods:
$this->reflect(new \App\Listeners\SendWelcomeEmail())
->call('handle', [$user]);
State Management:
reflect() persists the reflection for the class instance’s lifecycle. Avoid reflecting the same instance multiple times in setUp() if not needed.on() creates a temporary reflection; changes won’t persist beyond the method call.Dynamic Method/Property Names:
// ❌ Fails if method is `calculateTotal`
$this->call('calculateTotalAmount');
Static Methods/Properties:
ReflectionClass::newInstanceWithoutConstructor() or ::callStatic() as fallbacks.Closures/Lambdas:
Performance:
on() for multiple calls on the same instance is less efficient than reflect(). Benchmark in CI:
// Prefer this for repeated calls:
$this->reflect($service);
$this->call('methodA');
$this->call('methodB');
Invalid Method/Property Errors: Verify the class/method exists using:
$this->assertMethodExists('App\Models\User', 'validateName');
(Note: Requires custom assertion; use ReflectionClass directly if needed.)
Reflection Exceptions:
Wrap calls in try-catch for graceful failures:
try {
$this->call('nonExistentMethod');
} catch (\ReflectionException $e) {
$this->fail($e->getMessage());
}
Custom Assertions:
Extend PHPUnit with helper methods:
protected function assertPrivateMethodReturns($instance, $method, $args, $expected)
{
$this->reflect($instance);
$actual = $this->call($method, $args);
$this->assertEquals($expected, $actual);
}
Mocking Reflection: For isolated tests, mock the trait’s methods:
$this->getMockBuilder(SKAgarwal\Reflection\ReflectableTrait::class)
->onlyMethods(['call'])
->getMock();
Laravel Artisan Commands:
Test protected handle() methods:
$this->reflect(Artisan::getInstance())
->call('fire', ['command:key:generate']);
Reflection API changes.composer.json:
"autoload": {
"psr-4": {
"SKAgarwal\\Reflection\\": "vendor/skagarwal/reflection/src"
}
}
Run composer dump-autoload if issues arise.src/ReflectableTrait.php (core logic)tests/ (test patterns for extension).mockery or phpunit/phpunit's built-in reflection tools:
$reflection = new \ReflectionMethod($class, 'privateMethod');
$reflection->setAccessible(true);
How can I help you explore Laravel packages today?