Showing Posts From
PHP
ThinkPHP 5.1 老项目 + Composer 2:think-installer 装不上
维护一个老 ThinkPHP 5.1 项目,composer install 直接崩: Problem 1 - topthink/think-installer is locked to version v2.0.0 and an update of this package was not requested. - topthink/think-installer v2.0.0 requires composer-plugin-api ^1.0 -> found composer-plugin-api[2.9.0] but it does not match the constraint. Problem 2 - topthink/framework v5.1.39 requires topthink/think-installer 2.* ...顺带还有: The "https://packagist.phpcomposer.com/packages.json" file could not be downloaded (404)两个问题:Composer 版本不兼容 + 镜像源死了。 症状 1:think-installer 只支持 Composer 1 topthink/think-installer v2.0.0 明确要 composer-plugin-api ^1.0,也就是 Composer 1.x 的插件接口。你机器上是 Composer 2.x,插件接口版本变成 2.x,直接不兼容。 TP5.1 上线那会儿(2018)Composer 2 还没出,这问题在当时不存在,遗留下来才成了坑。 解法:降回 Composer 1 推荐、最省事、也最不折腾业务代码。 方法 A:Composer 自己降版本 composer self-update --1Composer 会在原地切到最新的 1.x(一般是 1.10.27)。想指定: composer self-update 1.10.27方法 B:装一个独立的 Composer 1 不想动全局 Composer 的话,单独下一份: curl -sS https://getcomposer.org/installer | php -- --1 mv composer.phar /usr/local/bin/composer1 composer1 install老项目用 composer1,新项目继续用 composer。 症状 2:packagist.phpcomposer.com 已凉 老教程里推荐的 packagist.phpcomposer.com 这个镜像已经关服多年,仍然响应 404。检查 composer.json: cat composer.json | grep -A 3 repositories看到: "repositories": { "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com" } }改成官方源或者阿里镜像: # 官方 composer config -g repo.packagist composer https://repo.packagist.org# 阿里云 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/# 清华(有时更快) composer config -g repo.packagist composer https://mirrors.tuna.tsinghua.edu.cn/composer/-g 是全局,改单个项目就去掉。 也可以直接改 composer.json 里的 repositories 字段,同样效果。 完整恢复流程 # 1. Composer 降 1 composer self-update --1# 2. 换镜像 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/# 3. 清缓存 composer clear-cache# 4. 重装依赖 rm -rf vendor composer.lock composer install90% 的老 TP5.1 项目这一套下来就恢复了。 什么时候该升级项目 如果这不是紧急救火,考虑:升到 ThinkPHP 6 或 8,从 5.1 迁移文档官方有 或者只把 topthink/think-installer 换成 topthink/framework 的现代版本,去掉这个已废弃的插件但生产环境急救时先别动这些——先降 Composer 让站起来,稳定了再排期改造。 一句话总结 think-installer 只兼容 Composer 1,最直接的解法是 composer self-update --1。顺手把死了的 phpcomposer.com 镜像换成阿里或官方源。
用 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.jsonapp/ 是业务代码、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-autoloadPSR-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 前先自己写一遍,再去看源码会很轻松。
