Skip to content

exit 和 die

exit 是一个语言结构(不是函数),用于立即终止当前脚本的执行。dieexit 的别名,两者功能完全相同。exit 可以接受一个可选的状态码或字符串消息参数。在生产环境中,exit 通常只在不可恢复的错误场景下使用。

前置知识

基础概念

exit 的核心行为:

  1. 立即终止当前 PHP 脚本的执行。
  2. 如果传入了字符串,会先输出该字符串再终止。
  3. 如果传入了整数(0-254),作为退出状态码(CLI 模式下有意义)。
  4. 如果未传参数,默认状态码为 0(表示成功)。
  5. 即使在 include 的文件中调用,也会终止整个脚本(不仅仅是当前文件)。

与 return 的区别

include 文件中,return 只终止当前包含文件的执行并返回值给调用方。而 exit/die 会终止整个脚本,包括所有包含文件和后续代码。

语法结构

基本用法

php
<?php
declare(strict_types=1);

// 无参数:正常终止(状态码 0)
// exit;
// die;

// 传入整数:设置退出状态码
// exit(0);   // 成功
// exit(1);   // 一般错误
// exit(255); // 最大状态码值

// 传入字符串:输出消息后终止
// exit('脚本异常终止');
// die('发生致命错误');

输出消息后终止

php
<?php
declare(strict_types=1);

function checkEnvironment(): void
{
    $requiredExtension = 'json';

    if (!extension_loaded($requiredExtension)) {
        die("致命错误:缺少必需的扩展 {$requiredExtension}\n");
    }

    echo "环境检查通过\n";
}

checkEnvironment();
echo "这行代码会在环境检查通过后执行\n";

在函数中使用 exit

php
<?php
declare(strict_types=1);

function fatalError(string $message): never
{
    // PHP 8.1+ 的 never 返回类型
    echo "FATAL: {$message}\n";
    exit(1);
}

function criticalCheck(bool $condition): void
{
    if (!$condition) {
        exit("关键检查失败,脚本终止\n");
    }
    echo "关键检查通过\n";
}

// 使用
echo "脚本开始\n";
criticalCheck(false);
echo "这行不会执行\n";
// 输出:
// 脚本开始
// 关键检查失败,脚本终止

详细说明

状态码的含义

在 CLI 模式下,exit 的状态码可以被调用脚本的外部程序(如 shell 脚本、CI/CD 管道)检测。

状态码含义说明
0成功脚本正常执行完毕
1一般错误最常见的错误码
2用法错误命令行参数错误等
127命令未找到外部命令不存在
1-125自定义应用程序自定义错误码
126不可执行命令存在但无法执行
128+N信号终止被信号 N 终止
php
<?php
declare(strict_types=1);

// CLI 脚本中的状态码使用
$options = getopt('f:');

if (!isset($options['f'])) {
    fwrite(STDERR, "错误:缺少必需参数 -f\n");
    fwrite(STDERR, "用法:php script.php -f <filename>\n");
    exit(2); // 用法错误
}

$filename = $options['f'];

if (!file_exists($filename)) {
    fwrite(STDERR, "错误:文件不存在 {$filename}\n");
    exit(1); // 一般错误
}

echo "处理文件:{$filename}\n";
// 处理逻辑...
exit(0); // 成功

exit 与 return 在 include 中的区别

php
<?php
// file: library.php
declare(strict_types=1);

echo "库文件开始\n";

$value = '来自库文件的数据';

// 使用 return:只终止当前文件,主脚本继续
// return $value;

// 使用 exit:终止整个脚本
// exit('库文件终止了整个脚本');

echo "库文件结束\n";
php
<?php
// file: main.php
declare(strict_types=1);

echo "主脚本开始\n";

// 场景 1:library.php 使用 return
// 输出:主脚本开始 -> 库文件开始 -> 库文件结束 -> 主脚本继续
// $result = include 'library.php';

// 场景 2:library.php 使用 exit
// 输出:主脚本开始 -> 库文件开始 -> 库文件终止了整个脚本

echo "主脚本结束\n";

shutdown_function 与 exit

使用 register_shutdown_function 注册的回调函数在 exit 时仍然会被执行。

php
<?php
declare(strict_types=1);

register_shutdown_function(function (): void {
    echo "[shutdown] 清理资源...\n";
    echo "[shutdown] 记录日志...\n";
    echo "[shutdown] 脚本退出\n";
});

echo "脚本开始执行\n";

// 模拟一些操作
echo "执行业务逻辑...\n";

echo "准备退出\n";
exit(0);
// 输出:
// 脚本开始执行
// 执行业务逻辑...
// 准备退出
// [shutdown] 清理资源...
// [shutdown] 记录日志...
// [shutdown] 脚本退出

PHP 8.1+ 的 never 返回类型

PHP 8.1 引入了 never 返回类型,明确表示函数会通过 exitdie 或抛出异常来终止,永远不会正常返回。

php
<?php
declare(strict_types=1);

function abort(int $statusCode, string $message): never
{
    http_response_code($statusCode);
    echo json_encode([
        'error' => [
            'code'    => $statusCode,
            'message' => $message,
        ],
    ]);
    exit;
}

// 调用
abort(403, '访问被拒绝');
echo "这行不会执行\n";

实战示例

CLI 命令行工具的优雅退出

php
<?php
declare(strict_types=1);

function main(array $argv): int
{
    $command = $argv[1] ?? '';

    return match ($command) {
        'hello' => runHello($argv),
        'build' => runBuild($argv),
        'help'  => runHelp(),
        default => runUnknownCommand($command),
    };
}

function runHello(array $argv): int
{
    $name = $argv[2] ?? 'World';
    echo "Hello, {$name}!\n";
    return 0;
}

function runBuild(array $argv): int
{
    echo "构建项目...\n";
    // 模拟构建失败
    echo "错误:编译失败\n";
    return 1;
}

function runHelp(): int
{
    echo "用法:php cli.php <command> [args]\n";
    echo "命令:\n";
    echo "  hello [name]  - 打印问候语\n";
    echo "  build         - 构建项目\n";
    echo "  help          - 显示帮助\n";
    return 0;
}

function runUnknownCommand(string $command): int
{
    fwrite(STDERR, "未知命令:{$command}\n");
    return 127;
}

// 不直接 exit,而是返回状态码
$exitCode = main($argv ?? ['cli.php']);
exit($exitCode);

生产环境中的健康检查

php
<?php
declare(strict_types=1);

// 健康检查端点
function healthCheck(): void
{
    $checks = [
        'database' => checkDatabase(),
        'cache'    => checkCache(),
        'disk'     => checkDiskSpace(),
    ];

    $allHealthy = true;

    foreach ($checks as $service => $healthy) {
        $status = $healthy ? 'OK' : 'FAIL';
        echo "{$service}: {$status}\n";

        if (!$healthy) {
            $allHealthy = false;
        }
    }

    if (!$allHealthy) {
        http_response_code(503);
        exit(1);
    }

    http_response_code(200);
    exit(0);
}

// 模拟检查函数
function checkDatabase(): bool { return true; }
function checkCache(): bool { return true; }
function checkDiskSpace(): bool { return false; }

healthCheck();

注意事项

  1. dieexit 的别名:两者完全等价,但现代代码中推荐使用 exit,因为 die 的语义不够明确。

  2. exit 会绕过所有后续代码:即使在函数内部调用 exit,也会终止整个脚本。

  3. 析构函数仍会执行exit 时,已创建对象的析构方法(__destruct)和已注册的 shutdown 函数仍然会被调用。

  4. 不适合单元测试exit 使得代码难以测试。应将退出逻辑与业务逻辑分离。

  5. 在 Web 环境中谨慎使用:在 HTTP 响应中使用 exit 可能导致不完整的输出或缓冲区问题。

最佳实践

  1. 避免在生产代码中直接使用 exit:应该通过异常和错误处理机制来处理错误,而不是直接终止脚本。

  2. CLI 脚本返回状态码:在 CLI 脚本中,使用 exit 返回有意义的退出状态码,方便外部脚本判断执行结果。

  3. 集中退出点:将退出逻辑集中在程序入口处,避免在业务代码中散布 exit 调用。

  4. 使用 never 类型标注(PHP 8.1+):对于永远不会正常返回的函数(如终止脚本或抛出异常),使用 never 返回类型。

  5. 配置文件入口保护:在配置文件中使用 exit 防止直接访问。

下一节

除了 exit,PHP 还有 goto 用于跳转到指定位置。接下来学习 goto

进阶用法

调试与测试技巧

php
<?php
declare(strict_types=1);

// 单元测试辅助函数
function createTestResource(): mixed
{
    return match (true) {
        default => new stdClass(),
    };
}

// 调试输出函数
function debugOutput(mixed , string  = ''): void
{
     =  ? ": " : '';
     .= print_r(, true);
    fwrite(STDERR,  . "\n");
}

// 性能基准测试
function benchmark(callable , int  = 1000): float
{
     = hrtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $fn();
    }
    return (hrtime(true) - $start) / 1e9;
}

日志记录实践

php
<?php
declare(strict_types=1);

/**
 * 简易日志记录器
 */
class SimpleLogger
{
    private string $logFile;
    private string $level = 'INFO';

    public function __construct(string $logFile)
    {
        $this->logFile = $logFile;
    }

    public function info(string $message, array $context = []): void
    {
        $this->log('INFO', $message, $context);
    }

    public function warning(string $message, array $context = []): void
    {
        $this->log('WARNING', $message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->log('ERROR', $message, $context);
    }

    private function log(string $level, string $message, array $context): void
    {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
        $line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
}

配置与环境检测

php
<?php
declare(strict_types=1);

// 环境检测工具
class EnvironmentChecker
{
    public static function checkRequirements(array $requirements): array
    {
        $results = [];
        foreach ($requirements as $name => $check) {
            $results[$name] = is_callable($check) ? $check() : false;
        }
        return $results;
    }

    public static function getSystemInfo(): array
    {
        return [
            'php_version' => PHP_VERSION,
            'os' => PHP_OS,
            'sapi' => PHP_SAPI,
            'memory_limit' => ini_get('memory_limit'),
            'max_execution_time' => ini_get('max_execution_time'),
            'loaded_extensions' => get_loaded_extensions(),
        ];
    }
}

常见问题排查

问题可能原因解决方案
连接超时网络问题/配置错误检查配置,增加超时时间
权限不足文件/目录权限使用 chmod/chown 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 8.0__construct(public $x)
php
<?php
declare(strict_types=1);

// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
    if (version_compare(PHP_VERSION, $minVersion, '<')) {
        throw new RuntimeException(
            sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
        );
    }
}

ensureVersion('8.1.0');

参考链接