标签 副作用 下的文章

我打算用一段时间系统性地学习 PSR(PHP Standard Recommendation)规范,借这个机会,把一些长期似懂非懂的 PHP 细节彻底理一遍。
这不是背规范,而是搞清楚:这些规则为什么存在

本文聚焦 PSR-1


一、PHP 标签:只准用 <?php<?=

Files MUST use only <?php and <?= tags.

PSR-1 要求 PHP 文件只能使用两种标签:

<?php
<?= $name ?>

为什么?

历史遗留问题

PHP 曾经支持:

<% echo $a; %>

这种“短标签”依赖服务器配置,换个环境就可能直接挂掉

<?= 是安全的

<?=<?php echo ?> 的语法糖,从 PHP 5.4 起默认开启、不可关闭

echo 函数用于输出一个或多个字符串,可以快速在页面查看变量的值,类似于angular的插值(interpolation)表达式。

结论很简单:

为了代码在任何环境都能跑。

二、编码问题:UTF-8、Unicode、ASCII 和 BOM 到底啥关系?

Files MUST use only UTF-8 without BOM for PHP code.

这句话信息量很大,我们拆开讲。


ASCII:编码界的“祖师爷”

ASCII 只解决了一件事:

  • 英文字母
  • 阿拉伯数字
  • 少量符号(一些标点符号、运算符号,控制字符等)

完全不考虑中文、日文、法语等语言中的特殊字母


Unicode:只管编号,不管存储

Unicode 的目标是:

给世界上每一个字符分配一个唯一编号

比如:

  • → U+4F60
  • A → U+0041

Unicode 不规定这些编号在内存里怎么存。


UTF-8:最成功的实现方案

如果按照 Unicode 全量存储,存英文会非常浪费空间。于是诞生了 UTF-8

UTF-8 的设计非常聪明:

  • 英文 → 1 字节
  • 中文 → 3 字节
  • 向下兼容 ASCII
  • 空间效率高

所以它成了目前互联网上最流行的标准。


BOM 是什么?为什么要禁止?

注意:这里的 BOM 不是浏览器的 BOM,而是:

Byte Order Mark

BOM 是文件最前面的几个字节,用来标记编码类型。

问题在于:

  • PHP 是 边读边执行
  • BOM 会被当成普通字符
  • 普通字符 = 输出

结果就是:

session_start(); // 报错
header();        // 报错

不是 PHP 代码有问题,是:

BOM 抢先输出了内容

所以 PSR-1 直接一刀切:

PHP 文件必须是 UTF-8,但不能带 BOM

三、DOM ≠ BOM:别再搞混了

很多人第一次看到 BOM,以为和 DOM 有关系(比如我),这是个误区。

DOM 是什么?

DOM(Document Object Model)本质是:

浏览器把 HTML 转换成一棵可操作的对象树

HTML:

<p>你好 <span>世界</span></p>

内存中的 DOM 结构:

p
├── 文本节点:你好
└── span
    └── 文本节点:世界

类似于这种:

DOM

所以:JS 操作的不是字符串,而是对象。


BOM 又是什么?

BOM(Browser Object Model)是:

浏览器对外暴露的能力接口

比如:

  • 窗口大小
  • URL
  • 历史记录
  • 弹窗

关系很简单:

window
 ├── document (DOM)
 └── location / history / navigator ...

PSR-1 里的 BOM,和浏览器毫无关系


四、声明 ≠ 副作用:一个文件只能干一件事

Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both.

翻译过来是:

要么定义东西,要么执行事情,别混着来。

反面示例

<?php
// util.php

function connectDb() {
    // ...
}

echo "db ready";

这个文件:

  • 声明了函数
  • 又产生了输出

问题立刻就来了:

include 'util.php';
header('Location: /login');

PHP 是顺序执行的,include 的瞬间就输出了内容,HTTP Header 被污染。


正确方法

// util.php
function connectDb() {
    // ...
}
// bootstrap.php
connectDb();
echo 'db ready';

一句话总结:

声明文件是被动的,执行文件是主动的。

五、自动加载:为什么 PSR 强制使用?

Namespaces and classes MUST follow an autoloading PSR (PSR-4,PSR-0)

如果没有自动加载,我们只能这样写:

require 'User.php';
require 'Order.php';
require 'Service/UserService.php';

问题有三个:

  1. 顺序要自己维护
  2. 文件一多直接崩
  3. 重构成本极高

自动加载解决了什么?

PHP 提供了机制:

当你使用一个类时,才去加载它对应的文件

但前提是:

类名和文件路径之间必须有确定规则

PSR-0 / PSR-4 的作用

它们定义的不是语法,而是映射规则

App\Service\UserService
↓
src/Service/UserService.php

我们只需要:

new UserService();

剩下的交给自动加载器。


六、命名不是审美,而是统一标准

PSR-1 对 类名、常量、方法名 的格式约束,看起来很强制,实际上解决的是一个更底层的问题:

人在读代码时,如何快速分辨这是什么东西。

PHP 是弱类型、动态语言,语法层面给的信息非常少,于是只能靠命名来补。


类名:大驼峰,本质是类型声明

Class names MUST be declared in StudlyCaps.
class UserService {}
class OrderDetail {}
class HttpRequest {}

StudlyCaps(大驼峰)有一个核心目的:

让类在视觉上立刻和变量、函数区分开。

对比一下:

$userService = new UserService();

我们不用读上下文,也不用看定义:

  • 小写开头 → 变量
  • 大写开头 → 类

这在 PHP 里尤其重要,因为:

  • PHP 不要求文件名必须和类名一致
  • 不强制一文件一类
  • 没有编译期类型检查

因此,类名的首字母大写,本质是在弥补语言层面的信息缺失。


类常量:全大写

Class constants MUST be declared in all upper case with underscore separators.
class User
{
    public const STATUS_ACTIVE = 1;
    public const STATUS_DISABLED = 0;
}

类常量的语义不是变量,而是:

  • 枚举值
  • 状态定义
  • 业务规则
  • 不应被修改的事实

全大写的作用只有一个:

强制我们在阅读时停一下:这个量是不可变的规则。

下划线而不是驼峰,是为了增加扫读效率:

STATUS_EMAIL_NOT_VERIFIED

我们可以不用拆词、不用脑补,直接读。

这和 SQL、环境变量、配置项的命名语义是完全一致的。


方法名:小驼峰,强调行为

Method names MUST be declared in camelCase.
public function getUserById() {}
public function calculateTotalPrice() {}
public function isValid() {}

对比这两种:

$user->getName();
User::getName();

如果方法也用大写开头,它在视觉上会和类混在一起,读代码时需要额外判断:

  • 这是构造对象?
  • 还是在调用函数?

到这里,PSR-1 的所有规则就形成了一个完整闭环:

  • 文件级:标签、编码、BOM、副作用
  • 结构级:声明与执行分离
  • 系统级:自动加载
  • 语义级:命名即类型

写在最后

此前虽接触过 PHP 与 ThinkPHP 框架,但随着时间推移,不少语法细节与编码规范已渐渐模糊,唯独 MVC 这一核心开发思想留存下来。感谢这次梳理 PSR-1 规范的契机(也感谢潘老师的任务指引),让我得以跳出只记规则的浅层学习。

这次系统性复盘,不仅是重新熟悉 PHP 语法,更是理解规范存在的意义:好的编码规范从来不是束缚,而是降低协作成本、规避隐藏问题的底层逻辑。后续也会带着这种 “知其然更知其所以然” 的思路,继续梳理其他 PSR 规范,把 PHP 基础扎得更牢。