PHP Composer 与命名空间

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

PHP 8.4 · Composer 2.8

require 一堆文件的日子早就过去了。Composer 负责两件事:装第三方包、按命名空间自动加载你自己的类。

环境准备

需要 PHP 8.4 与 Composer 2.8。确认安装:

php -v
composer --version

初始化项目

mkdir blog-api && cd blog-api
composer init --name="demo/blog-api" --type=project --no-interaction

生成的 composer.json 大致是这样:

{
  "name": "demo/blog-api",
  "description": "示例项目",
  "type": "project",
  "require": {
    "php": ">=8.3"
  },
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  },
  "config": {
    "sort-packages": true
  }
}

autoload.psr-4 的规则:App\Foo\Bar 对应 src/Foo/Bar.php。一个前缀映射一个目录,前缀必须以反斜杠结尾,很容易写错。

命名空间

<?php
// src/Post/PostRepository.php
declare(strict_types=1);

namespace App\Post;

final class PostRepository
{
    public function all(): array
    {
        return [];
    }
}
<?php
// public/index.php
declare(strict_types=1);

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

use App\Post\PostRepository;

$repo = new PostRepository();
var_dump($repo->all());

要点:

  • 一个文件一个类,文件名与类名一致(大小写敏感,Linux 上写错就是找不到)。
  • namespace 必须是文件第一条语句(declare 除外)。
  • use 只是别名,不触发加载;真正加载由自动加载器按需完成。
  • 同一命名空间下的类可以直接用短名,跨命名空间必须 use 或写全限定名 \App\Post\PostRepository。

use 的几种写法:

use App\Post\PostRepository;                 // 导入类
use App\Post\PostRepository as Repo;         // 别名
use function App\Support\escape;             // 导入函数
use const App\Support\VERSION;               // 导入常量
use App\Post\{Post, PostRepository};         // 分组导入

安装第三方包

composer require guzzlehttp/guzzle
composer require --dev phpunit/phpunit
  • require 进 require 段,运行时必需;--dev 进 require-dev,部署时 composer install --no-dev 会跳过。
  • 装完自动更新 composer.lock。锁文件要提交,它记录每个包的确切版本。
  • vendor/ 不要提交,部署时按锁文件重装。

版本约束:

写法 含义
^3.9 >=3.9 <4.0,允许小版本升级
~3.9.2 >=3.9.2 <3.10,只允许补丁级
3.9.* 同上
>=3.9 <4.0 显式范围

^ 是最常用也最容易踩的:主版本升级可能带破坏性变更,composer update 之后一定要跑测试。

常用命令

composer install        # 按锁文件安装(无锁文件才解析 composer.json)
composer update         # 更新依赖并改写锁文件
composer update vendor/package  # 只更新一个包
composer dump-autoload  # 重新生成自动加载(新增类没生效时用)
composer outdated       # 列出可升级的包
composer why guzzlehttp/psr7  # 谁引入了这个包
composer validate       # 检查 composer.json 是否合法

CI 与部署环境:

composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction

--optimize-autoloader 生成 classmap,类多时加载更快;改了类名要重新生成。

自动加载自定义函数

PSR-4 只管类。函数、常量、需要提前执行的引导文件用 files:

{
  "autoload": {
    "psr-4": { "App\\": "src/" },
    "files": ["src/Support/helpers.php"]
  }
}

files 里的文件每次请求都会加载,别放重逻辑,也别在里面查数据库。

脚本

composer.json 的 scripts 可以固化常用命令:

{
  "scripts": {
    "test": "phpunit",
    "serve": "php -S localhost:8000 -t public"
  }
}
composer test
composer run serve

配合 post-install-cmd / post-update-cmd 可以做安装后的初始化(清缓存、生成代理类等),但不要放需要人工确认的操作。

生产环境注意

  • APP_ENV、数据库密码走环境变量,不进仓库;.env 加到 .gitignore,提供 .env.example。
  • 使用 composer.lock 安装,禁止在生产直接 composer update。
  • vendor/ 目录权限要可读不可写(除个别需要缓存的包)。
  • 版本锁定到 PHP 次版本时,部署机的 PHP 版本要一致,否则 composer install 可能因平台要求失败(--ignore-platform-reqs 是掩盖问题,不是解决)。

常见错误与注意事项

1. 命名空间与目录不匹配

App\Post\PostRepository 放到 src/Repository/PostRepository.php,只有 classmap 才能救;按 PSR-4 放对目录最省事。

2. 改了类名没重新 dump

生产环境开了 --optimize-autoloader 后,classmap 是静态的,新增类必须 composer dump-autoload -o。

3. 直接改 vendor/

下次 composer install 就没了。要改行为就 fork、打补丁(cweagans/composer-patches)或提 PR。

4. 用 * 或 dev-main 当依赖版本

构建不可复现,随时可能拉到不兼容的提交。生产依赖固定到 tag。

5. 忘了 require vendor/autoload.php

命令行脚本报 Class not found,多半是缺这一行,或者路径写成了相对路径(用 __DIR__)。

6. 在 files 里做副作用

比如启动 session、连数据库,导致 CLI 脚本也跟着跑。files 只放纯函数与常量。

7. 平台差异

开发机 PHP 8.4、服务器 8.2,composer install 直接失败。config.platform.php 可以显式声明目标版本:

{ "config": { "platform": { "php": "8.2.0" } } }

小结

  • Composer 管依赖,PSR-4 管命名空间到目录的映射。
  • 锁文件保证可复现,部署用 composer install --no-dev -o。
  • 版本约束优先 ^,升级主版本必须跑测试。
  • 函数与常量走 autoload.files,不要在里面写副作用。
  • 命名空间、目录、文件名三者一致,能省掉大部分「找不到类」。

下一步