🔥 Ví dụ thuần PHP – Method DI (không framework)

1. Service + Model

class UserService {
    public function getUser($id) {
        return "User #" . $id;
    }
}

class Logger {
    public function log($msg) {
        echo "[LOG] $msg\n";
    }
}

2. Controller (inject qua method)

class UserController {

    // method injection
    public function show(UserService $service, Logger $logger, $id) {
        $user = $service->getUser($id);
        $logger->log("Fetched user");

        return $user;
    }
}

3. Mini Container hỗ trợ method DI

class Container {

    public function make($class) {
        return new $class();
    }

    public function call($callable, $params = []) {
        // $callable = [object, 'method']
        $reflection = new ReflectionMethod($callable[0], $callable[1]);

        $dependencies = [];

        foreach ($reflection->getParameters() as $param) {

            $type = $param->getType();

            if ($type && !$type->isBuiltin()) {
                // resolve class
                $dependencies[] = $this->make($type->getName());
            } else {
                // lấy từ params truyền vào (route param kiểu Laravel)
                $name = $param->getName();
                $dependencies[] = $params[$name] ?? null;
            }
        }

        return $reflection->invokeArgs($callable[0], $dependencies);
    }
}

4. Run thử (giống Laravel controller dispatch)

$container = new Container();

$controller = $container->make(UserController::class);

// giả lập route param
$params = ['id' => 123];

$result = $container->call([$controller, 'show'], $params);

echo $result;

👉 Output:

[LOG] Fetched user
User #123

⚡ Bản chất quan trọng (cái bạn đang hỏi sâu)

Method DI = 2 nguồn param trộn vào:

Loại paramResolve kiểu gì
Class type-hintContainer new + inject
Scalar (id, slug...)Lấy từ input (route, request)

=> Đây chính là cách Laravel làm:

$container->call([$controller, 'method'], $routeParams);

💡 Điểm khác constructor vs method DI

Constructor DIMethod DI
Inject lúc newInject lúc call method
Luôn tồn tạiChỉ dùng khi cần
Dùng cho dependency "core"Dùng cho dependency "runtime"

⚠️ Insight quan trọng (ít người để ý)

  • Method DI thực ra không phải container tự gọi
  • Mà phải có dispatcher layer (router / job handler / event bus)

👉 Flow chuẩn:

Router → Container->make(Controller)
       → Container->call(method, params)