> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 向 about 添加包的诊断信息

> 讲解如何使用 Laravel 13 的 AboutCommand 以 CLI 和 JSON 显示包的配置状态，以及求值时机和长期维护时的注意事项。

当收到用户咨询时，让对方能够以统一的格式确认包的启用/禁用状态以及当前选择的驱动。使用 `AboutCommand::add()`，无需实现专用命令，就能在 `php artisan about` 的输出中添加包专用的分区。

官方文档中有注册的基本示例。本页会深入到 Laravel 13 的实现，探讨信息的收集时机、JSON 的类型、分区名称冲突以及测试中的注册状态。

## 在 Provider 中注册显示内容

下面的示例假设 `courier.enabled` 和 `courier.driver` 已经作为包的配置完成注册。配置的注册方法请参阅[包配置的合并与缓存](/zh-CN/advanced/package-config-merging)。

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Foundation\Console\AboutCommand;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if (! $this->app->runningInConsole()) {
            return;
        }

        AboutCommand::add('Acme Courier', fn () => [
            'Enabled' => AboutCommand::format(
                (bool) config('courier.enabled', false),
                console: fn (bool $enabled) => $enabled ? 'ENABLED' : 'OFF',
            ),
            'Driver' => (string) config('courier.driver', 'log'),
        ]);
    }
}
```

`runningInConsole()` 是用于避免在 HTTP 请求中进行不必要注册的条件。它并不只在执行 `about` 时成立，其他 Artisan 命令中也会进行注册。不过，上面闭包内的配置读取在注册时并不会执行。

<Warning>
  请明确选择要显示的项目。不要输出 API 密钥、访问令牌、包含认证信息的连接 URL 或整个配置数组。JSON 输出同样不会自动屏蔽敏感信息。处理咨询时，请让对方只分享包的分区，并在分享前确认其内容。
</Warning>

## 区分注册时机与求值时机

Laravel `v13.35.0` 的 `add()` 不会当场收集数据，而是将注册用的闭包添加到静态的 `$customDataResolvers` 中。执行 `about` 时才会组装用于显示的数据，并对已注册的数据获取闭包进行求值。

```mermaid theme={null}
flowchart TD
    A["Provider 的 boot()"] --> B["通过 add() 保存注册"]
    B --> C["执行 about"]
    C --> D["收集标准信息和附加分区"]
    D --> E["对数据获取闭包求值"]
    E --> F["通过 --only 筛选分区"]
    F --> G["以 CLI 或 JSON 显示"]
```

与其在 `add()` 外部预先读取配置值并固定到数组中，不如在闭包内获取，这样能够反映命令执行时的状态。另一方面，`--only` 筛选是在数据获取闭包求值**之后**进行的。

```bash theme={null}
php artisan about --only=environment
```

像这样即使只指定标准分区，上面 `Acme Courier` 的闭包也会被求值。不显示与不处理是两回事。

因此，附加信息应从配置值或轻量的本地状态中获取。如果加入对外部 API 的连通性检查、数据库查询、文件改写等操作，就连查看无关分区的命令也可能变慢或失败。请将连通性检查和修复拆分到专用的 Artisan 命令中。

<Tip>
  仅凭 `runningInConsole()` 条件，并不能保证延迟 Service Provider 的 `boot()` 会被执行。如果希望始终注册诊断信息，请将该注册放在立即加载的 Provider 中。只将服务绑定延迟化的配置方式请参阅[延迟 Service Provider](/zh-CN/advanced/deferred-provider)。
</Tip>

## 兼顾 CLI 显示与 JSON 类型

若只想查看包的信息，请指定将分区名称转换为小写 snake case 后的值。对于 `Acme Courier`，即为 `acme_courier`。

```bash theme={null}
php artisan about --only=acme_courier
php artisan about --only=environment,acme_courier
php artisan about --only=acme_courier --json
```

在上面的注册示例中，当 `courier.enabled` 为 `true`、`courier.driver` 为 `log` 时，JSON 的形式如下。

```json theme={null}
{
  "acme_courier": {
    "enabled": true,
    "driver": "log"
  }
}
```

在 CLI 中，`Enabled` 显示为 `ENABLED`。`AboutCommand::format()` 是一个可以分别指定 CLI 用的 `console` 和 JSON 用的 `json` 的辅助方法。上面的示例只指定了 `console`，因此 JSON 中返回原始的 boolean 值。无需将 CLI 的显示字符串原样用于 JSON。

| 对象 | 实现上的转换与处理 |
| - | - |
| `--only` 的值与分区名称 | 转为小写后再转换为 snake case 进行比较 |
| JSON 的分区键 | 将分区名称转换为 snake case |
| JSON 的项目键 | 将项目名称转为小写后再转换为 snake case |
| `format()` 的值 | CLI 中使用 `console`，JSON 中使用 `json`。未指定时使用原始值 |

像示例那样使用以空格分隔的普通英文单词作为名称，可以让筛选和 JSON 键更容易处理。自动化处理所引用的键可能会随显示名称的变更而改变，因此请在发布时确认兼容性。

## 为分区使用包专用的名称

`add()` 会向同一分区追加项目。即使指定了同名分区，也不会替换掉之前注册的全部内容。

请选择像 `Acme Courier` 这样能与其他包区分开的名称，并仅在必要时才向 Laravel 标准的 `Environment`、`Cache`、`Drivers`、`Storage` 中添加内容。

如果在同一分区中重复注册同名项目，CLI 中可能会保留为多行，而 JSON 中会合并到同一个键，只保留后注册的值。另外，即使写法不同，也请避免转换为 snake case 后成为相同键的名称。请将注册位置集中到一处，设计成在 CLI 和 JSON 中项目都唯一。

## 在测试中处理静态注册状态

`about` 开始执行时，用于显示的 `$data` 会被初始化，但附加信息的注册列表 `$customDataResolvers` 会被保留。这样每次都能根据相同的注册重新收集信息，但如果在同一个 PHP 进程中重复执行 Provider 的 `boot()`，注册可能会不断累积。

`AboutCommand::flushState()` 是清除**所有包的注册**以及显示用数据的方法。请不要为了避免自身分区重复而在生产环境的 Provider 中调用它，否则连其他包的诊断信息也会丢失。

如果在自己的测试基础设施中重建应用，请明确由谁负责在测试之间初始化状态。若要初始化，请在启动目标 Provider 之前进行，然后再完成所有必要的注册。另外也请确认现有的测试基础设施是否已经初始化了状态。

### 发布前的检查项

请在[使用 Orchestra Testbench 测试 Laravel 包](/zh-CN/advanced/package-testing)以及实际使用的应用中确认以下组合。

| 要确认的操作 | 期望的结果 |
| - | - |
| `about --only=acme_courier` | 仅显示包的分区，项目不重复 |
| `about --only=acme_courier --json` | `enabled` 以 boolean、`driver` 以 string 获取 |
| `about --only=environment` | 包的信息收集没有外部通信或副作用，正常结束 |
| 更改启用/禁用或驱动 | 显示值和 JSON 值与命令执行时的配置一致 |
| 在同一应用中执行 2 次 | 项目不累积，每次执行都重新收集信息 |
| 在同一 PHP 进程中重建应用 | 注册状态不会在测试之间泄漏，其他包的信息也不会缺失 |
| 存在配置缓存的状态 | 显示应用实际使用的配置值，而非已发布配置文件的内容 |

## 相关页面

<Columns cols={2}>
  <Card title="Laravel 包开发" icon="box" href="/zh-CN/advanced/package-development">
    确认 Provider 与资源注册的基础知识。
  </Card>

  <Card title="包配置的合并与缓存" icon="sliders" href="/zh-CN/advanced/package-config-merging">
    确认默认值、用户覆盖与配置缓存之间的关系。
  </Card>
</Columns>

## 参考的一手资料

* [Laravel 官方文档：从包向 about 添加信息](https://github.com/laravel/docs/blob/13.x/packages.md#about-artisan-command)
* [Laravel 官方文档：about 与 --only](https://github.com/laravel/docs/blob/13.x/configuration.md#the-about-command)
* [Laravel Framework v13.35.0: AboutCommand](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/AboutCommand.php)：注册、求值顺序、键转换、`format()`、`flushState()`。
* [Laravel Framework v13.35.0: format 的测试](https://github.com/laravel/framework/blob/v13.35.0/tests/Foundation/Console/AboutCommandTest.php)
* [Laravel Framework v13.35.0: JSON 输出的集成测试](https://github.com/laravel/framework/blob/v13.35.0/tests/Integration/Foundation/Console/AboutCommandTest.php)


## Related topics

- [Laravel 包开发](/zh-CN/advanced/package-development.md)
- [包的服务解析后钩子](/zh-CN/advanced/package-service-resolution.md)
- [Laravel Reverb](/zh-CN/reverb.md)
- [Laravel Doctor — 应用程序诊断工具](/zh-CN/blog/laravel-doctor-introduction.md)
- [包的版本兼容性管理](/zh-CN/advanced/package-versioning.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.