Skip to content

注释

注释是在代码中添加说明性文字的方式,这些文字会被 PHP 解析器完全忽略,不会被执行。良好的注释习惯能够显著提升代码的可读性和可维护性,是团队协作和长期项目维护的基础。

前置知识

基础概念

PHP 支持三种注释风格:

注释风格语法适用场景
单行注释(C++ 风格)// ...行内说明、临时禁用
单行注释(Shell 风格)# ...行内说明(较少使用)
多行注释(C 风格)/* ... */块说明、详细描述
文档注释(phpDoc)/** ... */API 文档生成

性能说明

注释不会影响 PHP 代码的执行性能。PHP 解析器在编译阶段会完全忽略注释内容,不占用运行时的处理时间。

单行注释

C++ 风格单行注释

双斜杠 // 是最常用的单行注释方式。从 // 开始到行末的所有内容都会被当作注释:

php
<?php
declare(strict_types=1);

// 这是一个完整的单行注释
$connectionTimeout = 30; // 行尾注释:设置连接超时时间

$retryAttempts = 3;     // 最大重试次数
$backoffInterval = 5;    // 重试间隔(秒)

// 注释可以独占一行
// 也可以在代码右侧
echo "Connection configured\n"; // 输出配置完成信息

单行注释在 // 之后到行末的内容都被忽略:

php
<?php
declare(strict_types=1);

$name = "PHP";  // 定义变量 name
// $age = 25;   // 这行代码被注释掉了,不会执行
$version = 8.3; // 当前 PHP 版本

echo $name;      // 输出变量

Shell 风格单行注释

井号 # 同样表示单行注释,效果与 // 类似:

php
<?php
declare(strict_types=1);

# 这是一个 Shell 风格的单行注释
$host = "localhost"; # 数据库主机地址
$port = 3306;        # 数据库端口

# 这种风格在 Shell 脚本中更常见
# 在 PHP 中推荐使用 // 风格
echo "Config loaded\n";

PHP 8.0+ 注意事项

自 PHP 8.0 起,#[ 具有特殊含义——它是属性(Attributes)语法。如果在 #[ 后面紧跟的内容不符合属性语法,将导致解析错误:

php
<?php
declare(strict_types=1);

// PHP 8.0 之前:这只是一个注释
// PHP 8.0+:这是属性语法,不是注释!
#[Route("/api/users", methods: ["GET"])]

// 如果确实要用 # 风格注释,避免以 [ 开头
// 安全的写法
# This is a safe comment

// 推荐使用 // 风格避免歧义
// This is always a comment

因此,推荐始终使用 // 风格的单行注释,避免与 PHP 8.0+ 的属性语法冲突。

多行注释

C 风格多行注释

多行注释以 /* 开始,以 */ 结束,可以跨越多行:

php
<?php
declare(strict_types=1);

/*
 * 这是一个多行注释
 * 可以跨越多行
 * 适合较长的说明文字
 */

$apiKey = "sk-xxxxxxxxxxxx";
$apiSecret = "secret-xxxxxxxxxxxx";

/* 也可以写在一行 */
$baseUrl = "https://api.example.com";

多行注释中不能嵌套另一个多行注释:

php
<?php
declare(strict_types=1);

/*
 * 外层注释开始
 * /* 内层注释 - 这会导致问题 */
 * 外层注释在这里结束了吗?实际上到第一个 */ 就结束了
 */

// 下面这行代码不在注释中,会正常执行
echo "This line is NOT commented\n";

嵌套多行注释陷阱

多行注释 /* */ 不支持嵌套。第一个 */ 会结束注释,后面的内容将被正常解析。如果需要临时注释掉包含多行注释的代码块,可以使用单行注释 // 来包裹。

一种常用的技巧是混合注释风格来实现"可切换的注释块":

php
<?php
declare(strict_types=1);

// 注意:以下技巧利用了 /* 覆盖 // 的特性
//*  删除第一个斜杠即可启用此代码块
if ($debug) {
    error_log("Debug mode is ON");
    var_dump($userData);
}
// */

要启用上面的代码块,只需删除 //* 中的一个 /,使其变为 /*,整个代码块就变成了注释。

多行注释在正则表达式中的陷阱

如果注释中包含正则表达式定界符 /,可能会意外提前结束注释:

php
<?php
declare(strict_types=1);

// 危险:正则表达式中的 */ 会意外结束注释
/*
$pattern = '/^(\d+)/';  // 这个 */ 会结束注释!
*/

// 安全的写法:使用不同的正则定界符
/*
$pattern = '#^(\d+)#';  // 使用 # 作为定界符
$pattern = '~^(\d+)~';  // 使用 ~ 作为定界符
*/

phpDoc 文档注释

phpDoc 是一种特殊的多行注释格式,以 /** 开始(注意多了一个星号)。它遵循特定的格式规范,可以由工具(如 PHPDocumentor、phpDocumentor2)自动生成 API 文档。

类和方法的文档注释

php
<?php
declare(strict_types=1);

/**
 * 用户服务类
 *
 * 提供用户相关的业务逻辑处理,包括用户的增删改查、
 * 认证授权等功能。
 *
 * @package App\Services
 * @author  Developer <dev@example.com>
 * @version 1.0.0
 * @since   2026-01-01
 */
class UserService
{
    /**
     * 用户数据仓库
     *
     * @var UserRepository
     */
    private UserRepository $repository;

    /**
     * 构造函数
     *
     * 注入用户数据仓库的依赖实例。
     *
     * @param UserRepository $repository 用户数据仓库
     */
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    /**
     * 根据用户 ID 获取用户信息
     *
     * 查询数据库中指定 ID 的用户记录,返回对应的 User 实体。
     * 如果用户不存在则返回 null。
     *
     * @param int $userId 用户的唯一标识符,必须为正整数
     *
     * @return User|null 用户实体对象,不存在时返回 null
     * @throws InvalidArgumentException 当 userId 不是正整数时
     */
    public function getUserById(int $userId): ?User
    {
        if ($userId <= 0) {
            throw new InvalidArgumentException("User ID must be positive");
        }

        return $this->repository->find($userId);
    }

    /**
     * 创建新用户
     *
     * @param string $name  用户名,长度 2~50 个字符
     * @param string $email 邮箱地址,必须符合邮箱格式
     *
     * @return User 新创建的用户实体
     * @throws RuntimeException 当创建失败时
     */
    public function createUser(string $name, string $email): User
    {
        $user = new User($name, $email);
        $this->repository->save($user);

        return $user;
    }
}

常用的 phpDoc 标签

标签用途示例
@param描述方法参数@param string $name 用户名
@return描述返回值@return User 用户实体
@throws描述可能抛出的异常@throws RuntimeException
@var描述类属性@var string
@deprecated标记为已弃用@deprecated 2.0 使用新方法替代
@see引用其他文档@see UserService::getUserById()
@since标记引入版本@since 1.5.0
@package所属包名@package App\Services
@author作者信息@author Zhang San <zs@example.com>

函数的文档注释

php
<?php
declare(strict_types=1);

/**
 * 计算两个日期之间的工作日天数
 *
 * 排除周末(周六和周日),计算两个日期之间的工作日数量。
 * 如果结束日期早于开始日期,返回 0。
 *
 * @param string $startDate 开始日期,格式 Y-m-d
 * @param string $endDate   结束日期,格式 Y-m-d
 *
 * @return int 工作日天数
 *
 * @example
 * calculateWorkdays("2026-07-01", "2026-07-10"); // 返回 7
 */
function calculateWorkdays(string $startDate, string $endDate): int
{
    $start = new DateTimeImmutable($startDate);
    $end = new DateTimeImmutable($endDate);

    if ($start > $end) {
        return 0;
    }

    $interval = new DateInterval("P1D");
    $period = new DatePeriod($start, $interval, $end->modify("+1 day"));

    $workdays = 0;
    foreach ($period as $day) {
        $weekday = (int)$day->format("N");
        if ($weekday < 6) {
            $workdays++;
        }
    }

    return $workdays;
}

详细说明

单行注释 // 与多行注释 /* */ 的交互

当不同风格的注释嵌套时,规则如下:一旦某种注释被打开,所有内容都会被忽略,直到该注释被关闭:

php
<?php
declare(strict_types=1);

// /* 这个 /* 在单行注释中,被忽略
// */ 这个 */ 也在单行注释中,被忽略
echo "A\n"; // 正常输出

/* // 这个 // 在多行注释中,被忽略
// */ 这个 */ 结束了多行注释
echo "B\n"; // 正常输出

HTML 注释不会阻止 PHP 执行

HTML 注释 <!-- --> 对 PHP 解析器完全无效。注释内的 PHP 代码仍然会被执行:

php
<?php
declare(strict_types=1);

// 以下代码中的 PHP 会被执行!
// 仅在浏览器端不可见(如果作为 HTML 输出的话)
// <!-- <?php echo "I am still executed!"; ?> -->

// 安全的做法:使用 PHP 注释来禁用代码
// <?php // echo "I am truly commented out"; ?>

注释中的结束标签陷阱

关键陷阱

在单行注释 //# 中,?> 仍然会被解析为 PHP 结束标签!只有 /* */ 多行注释不受此影响。

php
<?php
declare(strict_types=1);

// 危险:// 注释中的 ?> 会被当作结束标签
// $content = '<?php die(); ?>' . "\n";
// 上面的 ?> 结束了 PHP 模式,后面的部分被当作 HTML

// 安全写法 1:使用多行注释
/*
$content = '<?php die(); ?>' . "\n";
*/

// 安全写法 2:拼接避免
$content = '<' . '?php die(); ?' . '>' . "\n";

临时禁用代码的技巧

php
<?php
declare(strict_types=1);

// 技巧 1:使用可切换注释块
//*
echo "Block A is active\n";
//*/
/*/
echo "Block B is active\n";
// */

// 删除 //*/ 中的 / 即切换为注释状态
// 此时 Block A 被注释,Block B 执行

// 技巧 2:使用 if(false) 条件块
if (false) {
    echo "这段代码不会执行\n";
    $oldLogic = true;
}

实战示例

项目文件头的标准注释格式

php
<?php
declare(strict_types=1);

/**
 * 用户认证控制器
 *
 * 处理用户登录、注册、密码重置等认证相关的 HTTP 请求。
 * 基于 JWT 令牌实现无状态认证。
 *
 * @copyright 2026 My Company
 * @license   MIT License
 */

namespace App\Http\Controllers;

use App\Http\Requests\LoginRequest;
use App\Http\Requests\RegisterRequest;
use App\Services\AuthService;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller;

/**
 * 认证控制器
 *
 * 负责处理用户认证相关的 API 端点
 */
class AuthController extends Controller
{
    /**
     * 认证服务实例
     */
    public function __construct(
        private readonly AuthService $authService
    ) {
    }

    /**
     * 用户登录
     *
     * 验证用户凭据并返回 JWT 令牌。
     *
     * @param LoginRequest $request 登录请求对象
     *
     * @return JsonResponse 包含令牌和用户信息的 JSON 响应
     */
    public function login(LoginRequest $request): JsonResponse
    {
        $token = $this->authService->authenticate(
            $request->validated("email"),
            $request->validated("password")
        );

        return response()->json([
            "token" => $token,
            "token_type" => "bearer",
            "expires_in" => config("auth.jwt.ttl") * 60,
        ]);
    }

    /**
     * 用户注册
     *
     * 创建新用户账户并返回认证令牌。
     *
     * @param RegisterRequest $request 注册请求对象
     *
     * @return JsonResponse 包含令牌和用户信息的 JSON 响应
     */
    public function register(RegisterRequest $request): JsonResponse
    {
        $user = $this->authService->register(
            $request->validated("name"),
            $request->validated("email"),
            $request->validated("password")
        );

        return response()->json([
            "user" => $user,
            "message" => "注册成功",
        ], 201);
    }
}

代码审查注释规范

php
<?php
declare(strict_types=1);

class PaymentService
{
    /**
     * 处理支付请求
     *
     * @param array $paymentData 支付数据
     *
     * @return bool 支付是否成功
     */
    public function processPayment(array $paymentData): bool
    {
        // TODO: 添加支付金额上限校验(预计 v2.1 完成)
        // FIXME: 当货币类型为 JPY 时,小数点处理有 bug
        $amount = $paymentData["amount"];
        $currency = $paymentData["currency"];

        // HACK: 临时方案 - 等上游 API 修复后移除
        if ($currency === "JPY") {
            $amount = (int)round($amount);
        }

        // NOTE: 第三方支付网关要求金额以分为单位
        $amountInCents = (int)($amount * 100);

        return $this->callPaymentGateway($amountInCents);
    }
}

常用的审查注释标记:

标记含义用途
TODO待完成标记需要后续实现的功能
FIXME需修复标记已知的问题或 bug
HACK临时方案标记不优雅但暂可用的代码
NOTE备忘提醒重要的实现细节
XXX危险/需要审查标记需要特别注意的代码
OPTIMIZE可优化标记有性能优化空间的代码

注意事项

1. 注释不是越多越好

注释应该解释"为什么"而不是"是什么"。好的代码本身就能说明"是什么":

php
<?php
declare(strict_types=1);

// 不好:注释只是在重复代码
// 将 a 设置为 5
$a = 5;

// 好:解释为什么是 5
// 每页显示 5 条记录,符合用户研究的最优阅读体验
$recordsPerPage = 5;

// 更好:使用有意义的变量名,甚至不需要注释
$optimalRecordsPerViewport = 5;

2. 避免注释掉的代码积累

不应该在代码库中保留大量被注释掉的代码。使用版本控制系统(如 Git)来管理历史代码:

php
<?php
declare(strict_types=1);

// 不好的做法:大量注释掉的代码
// $oldQuery = "SELECT * FROM users WHERE status = 1";
// $result = $db->query($oldQuery);
// while ($row = $result->fetch()) {
//     echo $row["name"];
// }
// $newLogic = true;

// 好的做法:直接删除,从 Git 历史中恢复
$activeUsers = $userRepository->findByStatus(UserStatus::Active);

3. 保持注释与代码同步

过时的注释比没有注释更糟糕。修改代码时务必同步更新注释:

php
<?php
declare(strict_types=1);

// 错误示例:注释与代码不符
// 返回所有活跃用户(包括管理员)
function getActiveUsers(): array
{
    // 实际代码排除了管理员
    return $this->users->where("status", "active")
        ->where("role", "!=", "admin")
        ->get()
        ->toArray();
}

4. 文档注释中的类型标注要与实际一致

php
<?php
declare(strict_types=1);

/**
 * 获取用户年龄
 *
 * @param int $userId 用户 ID
 *
 * @return int 用户年龄
 * @throws UserNotFoundException 用户不存在时抛出
 */
public function getUserAge(int $userId): int
{
    // 确保文档中的类型和异常与实际代码一致
    $user = $this->findById($userId);
    // ...
}

最佳实践

  1. 使用 // 作为首选单行注释风格:避免与 PHP 8.0+ 属性语法 #[...] 冲突
  2. 文档注释使用 /** */ 格式:为类、方法、属性编写 phpDoc 文档
  3. 注释解释"为什么"而非"是什么":让代码自解释,注释补充设计意图
  4. 定期清理过时注释:保持注释与代码同步,删除无用的注释代码
  5. 使用 TODO/FIXME 标记待办事项:配合 IDE 插件高效追踪
  6. 利用 phpDoc 生成 API 文档:使用 phpDocumentor 或 IDE 智能提示
  7. 注释中使用英文或保持一致性:根据团队规范选择统一的语言
  8. 不在注释中包含敏感信息:密码、API 密钥等不应出现在注释中

下一节

在掌握了 PHP 的基本语法、标签、指令分隔符和注释之后,我们即将进入变量相关的话题。下一节将学习变量的基础概念,包括命名规则、赋值方式、类型推导和引用赋值。请阅读 变量基础

参考链接