用 Composer 从零搭一个 PHP mini 框架:路由 + PSR-4 + 容器

想理解 Laravel、Symfony 是怎么跑起来的,别一头扎进它们的源码——十几万行随便一个 ServiceProvider 就把人绕晕。自己用 Composer 手写一个 100 行的 mini 框架,把路由、自动加载、IoC 弄明白,再去看大框架就轻松了。

目录结构

my-framework/
├── app/
│   └── Controller/
│       └── HomeController.php
├── public/
│   └── index.php
├── src/
│   ├── Router.php
│   ├── Container.php
│   └── Response.php
├── vendor/               # composer install 后生成
└── composer.json

app/ 是业务代码、src/ 是框架核心、public/ 是入口。

composer.json 与 PSR-4

mkdir my-framework && cd my-framework
composer init

composer.json

{
    "name": "you/mini-framework",
    "type": "project",
    "require": {
        "php": ">=8.1"
    },
    "autoload": {
        "psr-4": {
            "App\\":       "app/",
            "Framework\\": "src/"
        }
    }
}

生成自动加载器:

composer dump-autoload

PSR-4 的规则很简单:类的命名空间路径 = 文件相对路径App\Controller\HomeController 就在 app/Controller/HomeController.php

Router:50 行的路由

src/Router.php

<?php
namespace Framework;

class Router
{
    private array $routes = [];

    public function get(string $path, callable|array $handler): void
    {
        $this->routes['GET'][$path] = $handler;
    }

    public function post(string $path, callable|array $handler): void
    {
        $this->routes['POST'][$path] = $handler;
    }

    public function dispatch(): void
    {
        $method = $_SERVER['REQUEST_METHOD'];
        $path   = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

        $handler = $this->routes[$method][$path] ?? null;
        if (!$handler) {
            http_response_code(404);
            echo "404 Not Found";
            return;
        }

        // 支持 [Controller::class, 'method']
        if (is_array($handler)) {
            [$class, $method] = $handler;
            $instance = new $class();
            echo $instance->$method();
            return;
        }

        // 支持匿名函数
        echo $handler();
    }
}

入口

public/index.php

<?php
require __DIR__ . '/../vendor/autoload.php';

use Framework\Router;
use App\Controller\HomeController;

$router = new Router();

$router->get('/', fn() => "Hello Framework");
$router->get('/home', [HomeController::class, 'index']);

$router->dispatch();

Controller:

<?php
namespace App\Controller;

class HomeController
{
    public function index(): string
    {
        return "home page";
    }
}

跑起来:

php -S localhost:8000 -t public

访问 http://localhost:8000//home 都能看到内容。

到这里,Laravel 最核心的三件事你都实现了:Composer PSR-4 加载、路由、控制器分发。

第二步:加个 IoC 容器

Laravel 的灵魂其实不是路由,是容器(Container / Service Container)。它做两件事:

  1. 帮你 new 对象,自动注入构造参数
  2. 让你把接口绑定到具体实现,实现依赖倒置

最简版本:

<?php
namespace Framework;

use ReflectionClass;

class Container
{
    private array $bindings = [];

    public function bind(string $abstract, callable|string $concrete): void
    {
        $this->bindings[$abstract] = $concrete;
    }

    public function make(string $class): object
    {
        // 1. 有绑定就走绑定
        if (isset($this->bindings[$class])) {
            $c = $this->bindings[$class];
            return is_callable($c) ? $c($this) : $this->make($c);
        }

        // 2. 反射构造函数依赖,递归 make
        $ref = new ReflectionClass($class);
        $ctor = $ref->getConstructor();
        if (!$ctor) return new $class();

        $args = [];
        foreach ($ctor->getParameters() as $p) {
            $type = $p->getType()?->getName();
            $args[] = $type ? $this->make($type) : null;
        }
        return $ref->newInstanceArgs($args);
    }
}

用法:

class Logger {
    public function log(string $msg): void { file_put_contents('log', $msg); }
}

class UserService {
    public function __construct(public Logger $logger) {}
}

$c = new Container();
$user = $c->make(UserService::class);   // 自动注入 Logger
$user->logger->log("hello");

这 40 行代码就是 Laravel Container 的核心思路。真正的 Laravel 加了:方法注入、上下文绑定、单例、tag、event、缓存反射等等,本质还是这个。

后续可以往上加

按需实现,每加一个都是一次深度理解:

  • Middleware — 前后置拦截
  • Request/Response 对象
  • View 层(模板引擎或 Blade 简版)
  • ORM(先用 PDO 封个 Query Builder)
  • Event / Listener
  • CLI 命令(symfony/console 或自己写)
  • Config 加载
  • 环境变量(vlucas/phpdotenv

一步步加,你会发现 Laravel 每个模块都能对应上。

一句话总结

Composer PSR-4 + Router + Container,三件套加起来 100 行代码,就是一个能跑的 mini 框架。学 Laravel 前先自己写一遍,再去看源码会很轻松。