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

# Laravel Prompts

> 介绍如何使用 laravel/prompts 扩展包为 Artisan 命令添加美观的交互式 UI。涵盖文本输入、选择、搜索、autocomplete、任务日志、流式输出等多种函数。

## 简介

[Laravel Prompts](https://github.com/laravel/prompts) 是一个为命令行应用添加美观易用交互式表单的 PHP 扩展包。
它提供了接近浏览器表单体验的占位符文本、校验等能力。

由于可以在 Artisan 命令代码中直接调用，因此写出的与用户交互的代码既简洁又直观。

<Info>
  Laravel Prompts 支持 macOS、Linux 与 Windows（WSL）。
  在不支持的环境中会自动降级到备用行为。
</Info>

## 安装

Laravel Prompts 已随 Laravel 本体一起分发，无需单独安装。

若在其他 PHP 项目中使用，通过 Composer 安装即可。

```shell theme={null}
composer require laravel/prompts
```

## 基础 Prompt 函数

### text — 文本输入

`text()` 用来让用户输入一个字符串。

```php theme={null}
use function Laravel\Prompts\text;

$name = text('What is your name?');
```

可以设置占位符、默认值和提示。

```php theme={null}
$name = text(
    label: 'What is your name?',
    placeholder: 'E.g. Taylor Otwell',
    default: $user?->name,
    hint: 'This will be displayed on your profile.'
);
```

指定 `required` 可将输入设为必填，并自定义错误提示。

```php theme={null}
$name = text(
    label: 'What is your name?',
    required: 'Your name is required.'
);
```

`validate` 闭包用来做额外校验。返回错误消息即校验失败，返回 `null` 表示通过。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3 => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);
```

也可以以数组形式使用 Laravel 校验规则。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: ['name' => 'required|max:255|unique:users']
);
```

### textarea — 多行文本输入

`textarea()` 接受多行输入。

```php theme={null}
use function Laravel\Prompts\textarea;

$story = textarea('Tell me a story.');
```

### number — 数字输入

`number()` 用于接收数字，可通过上下方向键调整数值。

```php theme={null}
use function Laravel\Prompts\number;

$copies = number(
    label: 'How many copies would you like?',
    default: 1,
    validate: ['copies' => 'required|integer|min:1|max:100']
);
```

### password — 密码输入

`password()` 用法与文本输入相同，但输入内容不会显示在屏幕上。

```php theme={null}
use function Laravel\Prompts\password;

$password = password('What is your password?');
```

```php theme={null}
$password = password(
    label: 'What is your password?',
    placeholder: 'password',
    hint: 'Minimum 8 characters.',
    validate: fn (string $value) => strlen($value) < 8
        ? 'The password must be at least 8 characters.'
        : null
);
```

### confirm — 是/否确认

`confirm()` 让用户在两个选项中做出选择，返回 `true` 或 `false`。

```php theme={null}
use function Laravel\Prompts\confirm;

$confirmed = confirm('Do you accept the terms?');
```

可以自定义默认值和标签文案。

```php theme={null}
$confirmed = confirm(
    label: 'Do you accept the terms?',
    default: false,
    yes: 'I accept',
    no: 'I decline',
    hint: 'The terms must be accepted to continue.'
);
```

### select — 单选列表

`select()` 让用户从列表中选一个。

```php theme={null}
use function Laravel\Prompts\select;

$role = select(
    label: 'What role should the user have?',
    options: ['Member', 'Contributor', 'Owner'],
    default: 'Owner'
);
```

如果传入关联数组，返回值就是键而不是展示的标签。

```php theme={null}
$role = select(
    label: 'What role should the user have?',
    options: [
        'member'      => 'Member',
        'contributor' => 'Contributor',
        'owner'       => 'Owner',
    ],
    default: 'owner'
);
```

`scroll` 用来指定滚动前显示的选项数量（默认 5）。

```php theme={null}
$role = select(
    label: 'Which category would you like to assign?',
    options: Category::pluck('name', 'id'),
    scroll: 10
);
```

### multiselect — 多选

`multiselect()` 让用户同时选择多个选项。

```php theme={null}
use function Laravel\Prompts\multiselect;

$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete']
);
```

指定 `required` 可要求至少选择一个。

```php theme={null}
$permissions = multiselect(
    label: 'What permissions should be assigned?',
    options: ['Read', 'Create', 'Update', 'Delete'],
    required: 'At least one permission must be selected.'
);
```

### suggest — 带自动补全的输入

`suggest()` 会给出提示同时允许自由输入。

```php theme={null}
use function Laravel\Prompts\suggest;

$name = suggest(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle']
);
```

传入闭包时可以根据输入动态过滤候选项。

```php theme={null}
$name = suggest(
    label: 'What is your name?',
    options: fn (string $value) => collect(['Taylor', 'Tobi', 'Dries'])
        ->filter(fn ($name) => str_starts_with($name, $value))
        ->values()
        ->all()
);
```

### search — 动态检索

`search()` 会在用户每次输入时刷新候选列表。闭包返回的数组即为候选。

```php theme={null}
use function Laravel\Prompts\search;

$userId = search(
    label: 'Search for the user that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);
```

### multisearch — 动态多选

`multisearch()` 允许在动态搜索的基础上进行多选。

```php theme={null}
use function Laravel\Prompts\multisearch;

$userIds = multisearch(
    label: 'Search for the users that should receive the mail',
    options: fn (string $value) => strlen($value) > 0
        ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
        : []
);
```

### pause — 暂停

`pause()` 用来让用户按下 Enter 键，暂停执行。

```php theme={null}
use function Laravel\Prompts\pause;

pause('Press ENTER to continue.');
```

### autocomplete — 内联补全

`autocomplete()` 会以 ghost text（灰色影子文本）形式展示候选。与 `suggest()` 不同的是，它在用户每次输入时以 ghost text 显示匹配项，通过 `Tab` 或右方向键确认补全。

```php theme={null}
use function Laravel\Prompts\autocomplete;

$name = autocomplete(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim']
);
```

同样可以设置占位符、默认值和提示。

```php theme={null}
$name = autocomplete(
    label: 'What is your name?',
    options: ['Taylor', 'Dayle', 'Jess', 'Nuno', 'Tim'],
    placeholder: 'E.g. Taylor',
    default: $user?->name,
    hint: 'Use Tab to accept, up/down to cycle.'
);
```

传入闭包可根据输入动态生成候选。

```php theme={null}
$file = autocomplete(
    label: 'Which file?',
    options: fn (string $value) => collect($files)
        ->filter(fn ($file) => str_starts_with(strtolower($file), strtolower($value)))
        ->values()
        ->all(),
);
```

## 校验

所有 Prompt 函数都可以通过 `validate` 参数进行校验。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: fn (string $value) => match (true) {
        strlen($value) < 3  => 'The name must be at least 3 characters.',
        strlen($value) > 255 => 'The name must not exceed 255 characters.',
        default => null
    }
);
```

闭包返回字符串会作为错误显示并要求重新输入。返回 `null` 则表示校验通过。

也可以以数组形式使用 Laravel 校验规则。

```php theme={null}
$name = text(
    label: 'What is your name?',
    validate: ['name' => 'required|max:255|unique:users']
);
```

若要在校验前先转换输入，使用 `transform` 参数。

```php theme={null}
$name = text(
    label: 'What is your name?',
    transform: fn (string $value) => trim($value),
    validate: fn (string $value) => match (true) {
        strlen($value) === 0 => 'The name must not be empty.',
        default => null
    }
);
```

## 表单

`form()` 可以把多个 Prompt 打包成一个表单，用户可以在完成之前统一取消。

```php theme={null}
use function Laravel\Prompts\form;

$responses = form()
    ->text('What is your name?', required: true, name: 'name')
    ->password('What is your password?', validate: ['password' => 'min:8'], name: 'password')
    ->confirm('Do you accept the terms?')
    ->submit();

$name     = $responses['name'];
$password = $responses['password'];
$confirmed = $responses[2];
```

## 信息输出

以下函数用于输出带样式的信息。

```php theme={null}
use function Laravel\Prompts\info;
use function Laravel\Prompts\warning;
use function Laravel\Prompts\error;
use function Laravel\Prompts\alert;
use function Laravel\Prompts\note;

note('Prepare for launch.');
info('User created successfully.');
warning('This action cannot be undone.');
error('Something went wrong.');
alert('Critical failure detected!');
```

## Callout（提示框）

`callout()` 会用边框把标签和内容框起来。适合突出显示部署总结、错误详情、状态更新等重要信息。

```php theme={null}
use function Laravel\Prompts\callout;

callout(
    label: 'Environment Configured',
    content: 'Your application is running in production mode with 4 workers.',
);
```

在 `type` 参数中传入 `'warning'` 或 `'error'` 可以改变视觉样式。

```php theme={null}
callout(
    label: 'Deprecation Notice',
    content: 'The `--prefer-stable` flag will be removed in v4.0.',
    type: 'warning',
);

callout(
    label: 'Database Connection Failed',
    content: 'Could not connect to MySQL on 127.0.0.1:3306.',
    type: 'error',
);
```

通过 `info` 参数可以添加尾行。方便展示 ID、时间戳等元数据。

```php theme={null}
callout(
    label: 'Deployment Summary',
    content: 'Your application was deployed to production.',
    info: 'deploy-id: d4f8a2c',
);
```

### 富内容

以数组代替字符串时，可以构造结构化的富内容 callout。`Element` 类提供了创建标题、无序列表、有序列表、键值列表和链接的工厂方法。

```php theme={null}
use Laravel\Prompts\Elements\Element;
use function Laravel\Prompts\callout;

callout('Deployment Summary', [
    'Your application was deployed to production at 2024-03-15 14:32 UTC.',
    Element::heading('What Changed'),
    Element::bulletedList([
        'Migrated 3 pending database migrations',
        'Cleared and rebuilt route cache',
        'Restarted 4 queue workers',
    ]),
    Element::heading('Next Steps'),
    Element::numberedList([
        'Verify the health check endpoint at /up',
        'Monitor error rates for the next 15 minutes',
        'Confirm background jobs are processing',
    ]),
]);
```

`Element::keyValueList` 可以展示带标签的数据。

```php theme={null}
callout('Database Connection Failed', [
    'Could not connect to the database server.',
    Element::keyValueList([
        'Host'     => '127.0.0.1',
        'Port'     => '3306',
        'Database' => 'forge',
        'Status'   => 'Connection refused',
    ]),
], type: 'error');
```

在支持 [OSC 8](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) 的终端中，`Element::link` 可以生成可点击的超链接。可以只传 URL，也可以传 URL 和自定义标签。

```php theme={null}
callout('Server Health Check', [
    'Multiple services are reporting degraded performance.',
    Element::heading('Affected Services'),
    'Look here: '.Element::link('https://example.com/health', 'Health Dashboard'),
    Element::link('https://example.com/health'),
]);
```

省略标签时，URL 本身会作为链接文本显示。

## 表格显示

`table()` 用于以表格形式展示数据。

```php theme={null}
use function Laravel\Prompts\table;

table(
    headers: ['Name', 'Email'],
    rows: User::all(['name', 'email'])->toArray()
);
```

## Spin（加载中动画）

`spin()` 会在闭包执行期间显示加载指示器。

```php theme={null}
use function Laravel\Prompts\spin;

$response = spin(
    message: 'Fetching response...',
    callback: fn () => Http::get('http://example.com')
);
```

<Warning>
  使用 `spin()` 需要 `pcntl` PHP 扩展。若环境不支持，则不会显示 spinner。
</Warning>

## 进度条

`progress()` 可视化地展示循环处理进度。

```php theme={null}
use function Laravel\Prompts\progress;

$users = progress(
    label: 'Updating users',
    steps: User::all(),
    callback: fn ($user) => $this->performTask($user),
    hint: 'This may take some time.'
);
```

也可以手动控制进度条。

```php theme={null}
$progress = progress(label: 'Uploading files', steps: count($files));

$progress->start();

foreach ($files as $file) {
    $this->uploadFile($file);
    $progress->advance();
}

$progress->finish();
```

## 任务（Task）

`task()` 会在回调执行期间显示 spinner 和可滚动的实时输出区。非常适合包装依赖安装、部署脚本等长时间任务，可实时了解发生了什么。

```php theme={null}
use function Laravel\Prompts\task;

task(
    label: 'Installing dependencies',
    callback: function ($logger) {
        // 长时间运行的处理...
    }
);
```

回调会收到一个 `Logger` 实例，你可以实时输出日志或状态。

<Warning>
  `task()` 需要 `pcntl` PHP 扩展。若环境不支持，会降级为静态显示。
</Warning>

### 输出日志行

`line` 方法逐行写入可滚动输出区。

```php theme={null}
task(
    label: 'Installing dependencies',
    callback: function ($logger) {
        $logger->line('Resolving packages...');
        $logger->line('Downloading laravel/framework');
    }
);
```

### 状态消息

`success`、`warning`、`error` 可以在滚动区上方展示带高亮的固定消息。

```php theme={null}
task(
    label: 'Deploying application',
    callback: function ($logger) {
        $logger->line('Pulling latest changes...');
        $logger->success('Changes pulled!');

        $logger->line('Running migrations...');
        $logger->warning('No new migrations to run.');

        $logger->line('Clearing cache...');
        $logger->success('Cache cleared!');
    }
);
```

### 更新标签

`label` 方法可以在运行期间更新任务标签。`subLabel` 方法用来设置显示在标签下方的浅色副标签，传空字符串可清除副标签。也可以通过 `subLabel` 参数指定初始副标签。

```php theme={null}
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->subLabel('Building assets...');
        // ...
        $logger->subLabel('Running migrations...');
        // ...
        $logger->subLabel('');
    },
    subLabel: 'Preparing...'
);
```

### 流式输出文本

对于类似 AI 逐步生成响应的场景，可以用 `partial` 方法一段段流式输出。全部完成后调用 `commitPartial` 提交。

```php theme={null}
task(
    label: 'Generating response...',
    callback: function ($logger) {
        foreach ($words as $word) {
            $logger->partial($word . ' ');
        }

        $logger->commitPartial();
    }
);
```

### 输出上限与保留总结

默认最多显示 10 行滚动输出，可以通过 `limit` 参数自定义。想在任务结束后仍保留状态消息，请传 `keepSummary: true`。

```php theme={null}
task(
    label: 'Deploying',
    callback: function ($logger) {
        $logger->success('Assets built');
        $logger->success('Migrations complete');
    },
    limit: 20,
    keepSummary: true,
);
```

## Stream（流式输出）

`stream()` 可以将文本逐步展示到终端。非常适合展示 AI 生成的内容或分块到达的数据。

```php theme={null}
use function Laravel\Prompts\stream;

$stream = stream();

foreach ($words as $word) {
    $stream->append($word . ' ');
    usleep(25_000); // 模拟块之间的延迟
}

$stream->close();
```

`append` 方法以淡入效果向流中追加文本。全部内容 stream 完毕后调用 `close` 完成输出并恢复光标。

## 终端操作

### 设置终端标题

```php theme={null}
use function Laravel\Prompts\title;

title('My Application');
```

传入空字符串可以恢复默认标题。

```php theme={null}
title('');
```

### 清屏

```php theme={null}
use function Laravel\Prompts\clear;

clear();
```

## 终端注意事项

**终端宽度**：当标签、选项或校验消息超过终端列宽时会自动截断。若面向 80 列终端，建议控制在 74 个字符以内。

**终端高度**：对支持 `scroll` 参数的 Prompt，会自动计算以留出校验消息的空间，确保能装进终端高度。

## 降级（Fallback）

在不支持的环境（如非 WSL 的 Windows）中会自动降级。
默认情况下会调用 Laravel 内置的 `$this->ask()`、`$this->choice()` 等方法。

## 测试

Laravel Prompts 可以与 Pest 或 PHPUnit 结合使用。

```php theme={null}
use Laravel\Prompts\Prompt;

Prompt::fake(['Taylor', true]);

$name      = text('What is your name?');
$confirmed = confirm('Do you accept the terms?');

Prompt::assertOutputContains('What is your name?');
```

使用 Laravel 的 Artisan 测试辅助方法，也可以对信息输出函数进行断言。

```php theme={null}
// Pest
test('report generation', function () {
    $this->artisan('report:generate')
        ->expectsPromptsInfo('Welcome to the application!')
        ->expectsPromptsWarning('This action cannot be undone')
        ->expectsPromptsError('Something went wrong')
        ->expectsPromptsAlert('Important notice!')
        ->expectsPromptsTable(
            headers: ['Name', 'Email'],
            rows: [
                ['Taylor Otwell', 'taylor@example.com'],
            ]
        )
        ->assertExitCode(0);
});
```

```php theme={null}
// PHPUnit
public function test_report_generation(): void
{
    $this->artisan('report:generate')
        ->expectsPromptsInfo('Welcome to the application!')
        ->expectsPromptsWarning('This action cannot be undone')
        ->expectsPromptsTable(
            headers: ['Name', 'Email'],
            rows: [['Taylor Otwell', 'taylor@example.com']]
        )
        ->assertExitCode(0);
}
```

## 相关页面

<Columns cols={2}>
  <Card title="Artisan 控制台" icon="terminal" href="/zh/artisan">
    在 Artisan 命令中使用 Prompts
  </Card>
</Columns>


## Related topics

- [GitHub Copilot SDK for Laravel](/zh/packages/laravel-copilot-sdk/index.md)
- [流式输出](/zh/packages/laravel-copilot-sdk/streaming.md)
- [控制台测试](/zh/console-tests.md)
- [Laravel Chisel — 启动套件的安装后脚本库](/zh/blog/chisel-introduction.md)
- [测试 - GitHub Copilot SDK for Laravel](/zh/packages/laravel-copilot-sdk/fake.md)
