想理解 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)。它做两件事:
- 帮你
new对象,自动注入构造参数 - 让你把接口绑定到具体实现,实现依赖倒置
最简版本:
<?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 前先自己写一遍,再去看源码会很轻松。
