Reflection یک قابلیته که به برنامه اجازه میده در زمان اجرا (runtime) اطلاعاتی دربارهی کد بهدست بیاره.
مثلاً:
این متد چه پارامترهایی داره؟
پارامترها از چه نوعی هستن؟ (type-hint)
چه صفاتی (attributes) روی کلاس یا متد تعریف شده؟
و حتی میتونه متد رو صدا بزنه یا خاصیت رو بخونه یا تغییر بده!
✅ چطوری لاراول از Reflection استفاده میکنه؟
فرض کن یه کنترلر داری:
class UserController extends Controller
{
public function sendOtp(Request $request, SmsService $sms)
{
// ...
}
}
وقتی کاربر /send-otp رو صدا میزنه، لاراول میفهمه باید sendOtp رو اجرا کنه. حالا سوال: چطوری؟
لاراول نمیدونه SmsService چیه — ولی چون این رو در type-hint میبینه، از Reflection استفاده میکنه تا امضای متد رو بررسی کنه:
ReflectionMethod::getParameters()
میگه: خب، این متد دو تا پارامتر داره:
$request از نوع Illuminate\Http\Request
$sms از نوع App\Services\SmsService
لاراول میگه: پس من باید از container این دو کلاس رو درست کنم یا از قبل داشته باشم.
حالا Laravel Service Container چی کار میکنه؟
$app->make(SmsService::class);
این خط، توی Service Container میگرده:
آیا SmsService قبلاً singleton شده؟
اگر بله، همونو بده.
اگر نه، باید یک instance بسازه. ولی SmsService ممکنه constructor داشته باشه!
در این لحظه، لاراول دوباره از Reflection استفاده میکنه، میبینه constructor SmsService چه پارامترهایی داره، مثلاً:
class SmsService
{
public function __construct(LoggerInterface $logger) { ... }
}
میفهمه: برای ساخت SmsService باید اول LoggerInterface رو resolve کنه، و همینطوری ادامه میده تا همه dependency chain درست شه.
در نهایت...
لاراول تمام این مقادیر رو ساخته، و میتونه متد sendOtp رو صدا بزنه:
$controller->sendOtp($request, $sms);
بدون اینکه تو نیاز باشه دستی چیزی بسازی!
خلاصه
📩 درخواست HTTP میاد
📌 Route متد UserController@sendOtp رو پیدا میکنه
🔍 لاراول با Reflection میفهمه sendOtp() چه پارامترهایی داره
🧠 از Service Container کمک میگیره تا اون پارامترها (مثل SmsService) رو بسازه
🚀 متد رو با پارامترهای آماده اجرا میکنه
اگه دوست داشتی با یک مثال real کد ReflectionClass و ReflectionMethod هم نشونت بدم که خودت تو tinker اجرا کنی و ببینی دقیقاً چطوری امضای متد رو میخونه. بگی تا بنویسم برات.
بیایم با یه مثال ساده شروع کنیم که با ReflectionClass و ReflectionMethod امضای متد و type-hintها رو بررسی کنیم.
سناریو:
یه کلاس ساده داریم به نام SmsService که قراره تزریق بشه:
// app/Services/SmsService.php
namespace App\Services;
class SmsService
{
public function send(string $number, string $message)
{
// ...
}
}
و یه کنترلر داریم:
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\SmsService;
class UserController extends Controller
{
public function sendOtp(Request $request, SmsService $sms)
{
// ...
}
}
حالا بیایم داخل Tinker یا هر جای دیگه، با Reflection امضای متد sendOtp رو بررسی کنیم:
$reflection = new ReflectionMethod(\App\Http\Controllers\UserController::class, 'sendOtp');
foreach ($reflection->getParameters() as $param) {
echo "پارامتر: $" . $param->getName() . PHP_EOL;
$type = $param->getType();
if ($type && !$type->isBuiltin()) {
echo " - نوع: " . $type->getName() . PHP_EOL;
} else {
echo " - نوع: ساده یا تعریفنشده" . PHP_EOL;
}
}
خروجی:
پارامتر: $request
- نوع: Illuminate\Http\Request
پارامتر: $sms
- نوع: App\Services\SmsService
لاراول دقیقاً همین کارو میکنه تا بفهمه چه کلاسهایی رو باید resolve کنه.
🔁 اگه کلاس SmsService هم وابستگی داشته باشه چی؟
فرض کن SmsService اینطوری باشه:
class SmsService
{
public function __construct(\Psr\Log\LoggerInterface $logger) { }
}
بازم لاراول ReflectionClass(SmsService::class)->getConstructor() رو چک میکنه و دوباره اون پارامترها رو resolve میکنه.