PSR-1 编码标准
PSR-1(PHP Standard Recommendation 1)是 PHP-FIG(PHP Framework Interop Group)制定的最基础的编码标准,定义了 PHP 源代码的基本规范。遵循 PSR-1 能确保不同项目、不同团队之间的代码具有一致的基本结构,提高代码的可读性和可维护性。本节将详细介绍 PSR-1 的每条规则,并提供正确与错误的代码示例对比。
基础概念
什么是 PHP-FIG
PHP-FIG(PHP Framework Interop Group,PHP 框架互操作组织)是一个由多个知名 PHP 项目代表组成的联盟。它的目标是推动 PHP 生态系统的互操作性,通过制定和推广标准化接口(PSR)来减少不同项目之间的摩擦。
PSR-1 的定位
PSR-1 是最基础的编码规范,仅涵盖最核心、最不可争议的规则。更详细的格式规范由 PSR-12 提供。
PHP 编码规范层级:
PSR-1 → 基础编码标准(本节)
PSR-12 → 扩展编码样式(下一节)详细说明
1. 文件编码
PHP 文件必须只使用 UTF-8 编码,不得包含 BOM(Byte Order Mark)。
<?php
// ✅ 正确:UTF-8 无 BOM
// 文件以 <?php 开头
// ❌ 错误:包含 BOM 头
// EF BB BF <?php ... (十六进制的 BOM 标记)BOM 的影响
BOM 头会在 PHP 输出前产生不可见的字节,导致 header() 函数失败、Session 启动失败、JSON 输出格式错误等问题。确保编辑器设置为 "UTF-8 without BOM"。
2. 副作用(Side Effects)
PHP 文件应该只做一件事:要么定义符号(类、函数、常量等),要么产生副作用(输出内容、修改 ini 配置等),不应两者兼有。
<?php
// ❌ 错误:副作用与符号定义混在一起
$color = 'blue'; // 副作用
class Foo // 符号定义
{
public function bar(): void
{
// ...
}
}
echo $color; // 副作用
// ✅ 正确:仅定义符号
class Foo
{
public function bar(): void
{
// ...
}
}
// ✅ 正确:仅产生副作用
ini_set('display_errors', '1');
echo 'Hello World';什么算"副作用"
以下操作被认为是副作用:
- 生成输出(echo、print、
?>标签外的 HTML) - 直接使用
require/include(除非用于条件加载) - 修改 ini 配置(ini_set)
- 发送 HTTP 头(header)
- 操作文件/网络/数据库
- 定义自动加载器(spl_autoload_register)
3. 命名空间与类名
命名空间和类名必须遵循 PSR-4 自动加载规范。
<?php
// ✅ 正确:遵循 PSR-4
namespace Vendor\Package;
class ClassName
{
// ...
}
// ❌ 错误:不符合 PSR-4
namespace Vendor_Package;
class class_name
{
// ...
}- 类名必须使用
StudlyCaps(首字母大写的驼峰命名法) - PHP 5.3+ 必须使用命名空间
4. 类常量
类常量必须全部大写,单词之间用下划线分隔。
<?php
declare(strict_types=1);
namespace Vendor\Package;
class ClassName
{
// ✅ 正确
public const VERSION = '1.0.0';
public const MAX_RETRIES = 5;
public const DEFAULT_TIMEOUT = 30;
// ❌ 错误
public const version = '1.0.0';
public const MaxRetries = 5;
public const default_timeout = 30;
}PHP 8.1+ 的 readonly 常量
PHP 8.1 引入了 readonly 属性和 final 类常量。常量始终是公开且不可变的。
5. 属性命名
属性名必须使用 $camelCase(小写驼峰命名法)。
<?php
declare(strict_types=1);
namespace Vendor\Package;
class ClassName
{
// ✅ 正确
public string $firstName;
public int $maxRetries;
private bool $isEnabled;
protected array $userList = [];
// ❌ 错误
public string $first_name; // 下划线命名
public string $FirstName; // 首字母大写
public string $FIRSTNAME; // 全大写
public int $MAX_RETRIES; // 全大写
}6. 方法名
方法名必须使用 camelCase()(小写驼峰命名法)。
<?php
declare(strict_types=1);
namespace Vendor\Package;
class ClassName
{
// ✅ 正确
public function getUserById(int $id): ?User
{
return null;
}
public function processPayment(float $amount): bool
{
return true;
}
private function validateInput(array $data): void
{
// ...
}
// ❌ 错误
public function get_user_by_id(int $id): ?User { } // 下划线命名
public function GetUserId(int $id): ?User { } // 首字母大写
public function GET_USER(int $id): ?User { } // 全大写
}实战示例
场景一:PSR-1 完全合规的类文件
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\User;
use App\Repositories\UserRepository;
use Psr\Log\LoggerInterface;
class UserService
{
public const MAX_LOGIN_ATTEMPTS = 5;
public const SESSION_LIFETIME = 3600;
private UserRepository $userRepository;
private LoggerInterface $logger;
private int $attemptCount = 0;
public function __construct(
UserRepository $userRepository,
LoggerInterface $logger
) {
$this->userRepository = $userRepository;
$this->logger = $logger;
}
public function findUserById(int $id): ?User
{
return $this->userRepository->findById($id);
}
public function authenticate(string $email, string $password): bool
{
$user = $this->userRepository->findByEmail($email);
if ($user === null || !$user->verifyPassword($password)) {
$this->attemptCount++;
$this->logger->warning('Login failed', [
'email' => $email,
'attempt' => $this->attemptCount,
]);
return false;
}
$this->attemptCount = 0;
return true;
}
private function validateEmail(string $email): void
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email address');
}
}
}场景二:自动检测 PSR-1 违规
# 使用 PHP_CodeSniffer 检测 PSR-1 违规
composer require --dev squizlabs/php_codesniffer
vendor/bin/phpcs --standard=PSR1 src/
# 输出示例:
# FILE: src/Services/UserService.php
# --------------------------------------------------------------------------------
# FOUND 2 ERRORS AFFECTING 2 LINES
# --------------------------------------------------------------------------------
# 15 | ERROR | [x] Constant MAX_LOGIN_ATTEMPTS must be uppercase; expected MAX_LOGIN_ATTEMPTS
# 25 | ERROR | [x] Method name "get_user" is not in camelCase format
# --------------------------------------------------------------------------------# 自动修复(仅部分可修复)
vendor/bin/phpcbf --standard=PSR1 src/场景三:PSR-1 常见违规与修正
<?php
// 常见违规 #1:文件中混合副作用和类定义
$cache = new Redis();
class CacheService { }
// 修正:拆分为两个文件
// file1.php
$cache = new Redis();
// file2.php
class CacheService { }<?php
// 常见违规 #2:类名使用下划线
class user_service
{
public function get_user_by_id($id) { }
}
// 修正:遵循 StudlyCaps 和 camelCase
class UserService
{
public function getUserById(int $id): ?User { }
}<?php
// 常见违规 #3:类常量使用小写
class Config
{
const db_host = 'localhost';
const db_name = 'myapp';
}
// 修正:类常量全部大写
class Config
{
public const DB_HOST = 'localhost';
public const DB_NAME = 'myapp';
}场景四:PSR-1 速查表
┌──────────────────────┬──────────────────────────┐
│ 规则 │ 要求 │
├──────────────────────┼──────────────────────────┤
│ 文件编码 │ UTF-8 无 BOM │
│ 副作用 │ 仅定义符号 或 仅产生副作用 │
│ 类名 │ StudlyCaps │
│ 类常量 │ UPPER_SNAKE_CASE │
│ 属性名 │ camelCase │
│ 方法名 │ camelCase() │
│ 命名空间 │ 遵循 PSR-4 │
│ PHP 关键字 │ 全部小写 │
│ PHP 内置类型 │ 全部小写 │
│ PHP 8.1+ │ 支持 readonly/enum │
└──────────────────────┴──────────────────────────┘场景五:团队编码规范检查脚本
{
"scripts": {
"cs:check": "phpcs --standard=PSR1 --colors --report=full src/",
"cs:fix": "phpcbf --standard=PSR1 src/",
"cs:check-psr12": "phpcs --standard=PSR12 --colors --report=full src/"
}
}注意事项
1. PSR-1 是最低标准
PSR-1 仅规定了最基础的规则,实际项目中通常使用 PSR-12(或更严格的框架规范)作为代码风格标准。PSR-1 是所有 PHP 开发者都应该遵守的底线。
2. 工具自动化
手动检查 PSR-1 合规性既耗时又容易遗漏,推荐使用自动化工具:
# PHP_CodeSniffer(最常用)
vendor/bin/phpcs --standard=PSR1 src/
# PHP-CS-Fixer
vendor/bin/php-cs-fixer fix --rules=@PSR1 src/
# PHPStan(静态分析)
vendor/bin/phpstan analyse --level=1 src/3. 与 PSR-12 的关系
| 规范 | 范围 | 说明 |
|---|---|---|
| PSR-1 | 基础编码标准 | 最小公约数,所有 PHP 项目都应遵循 |
| PSR-12 | 扩展编码样式 | 基于PSR-1,增加了格式化、缩进等详细规则 |
实际开发建议
直接使用 PSR-12 作为代码风格标准,因为它包含 PSR-1 的所有规则并增加了更详细的格式规范。
最佳实践
1. 在 CI/CD 中强制检查
# .github/workflows/ci.yml
- name: Check PSR-1
run: vendor/bin/phpcs --standard=PSR1 src/2. IDE 自动格式化
配置 IDE 使用 PSR-12 格式化规则(自动包含 PSR-1):
- VS Code:安装
PHP-CS-Fixer或Intelephense扩展 - PhpStorm:Settings → Editor → Code Style → PHP → Set from... → PSR-12
3. pre-commit 钩子
# .git/hooks/pre-commit
#!/bin/bash
vendor/bin/phpcs --standard=PSR1 src/
if [ $? -ne 0 ]; then
echo "PSR-1 检查失败,请修复后再提交"
exit 1
fi下一节
继续学习:PSR-12 扩展编码样式 — 了解更详细的 PHP 编码格式规范,包括缩进、空白行、大括号位置等。