Skip to content

指令分隔符

在 PHP 中,语句(指令)之间使用分号 ; 进行分隔。和 C、Perl 等语言类似,PHP 要求每条语句以分号结束。然而,PHP 的结束标签 ?> 也隐含了一个分号,这使得在特定情况下可以省略最后一个分号。

前置知识

基础概念

PHP 中的"指令"(instruction)就是一条可执行的语句。每条指令需要以分号 ; 结尾,告诉解析器"这条语句到此结束"。分号是 PHP 语句的终止符,不是语句的分隔符——即使只有一条语句,也需要分号(除非它恰好位于 PHP 结束标签之前)。

分号的基本规则

php
<?php
declare(strict_types=1);

// 每条语句都以分号结尾
$name = "PHP";
echo $name;
$version = 8.3;

// 多条语句可以写在一行,用分号分隔
$a = 1; $b = 2; $c = 3;

// 分号表示语句结束,下面的代码在分号后的部分不会被解析为 PHP
echo "Hello"; // 这条语句正常结束

语句与分号

标准语句写法

在 PHP 代码段中,除最后一行外,每条语句都必须以分号结束:

php
<?php
declare(strict_types=1);

// 标准:每条语句以分号结束
$host = "localhost";
$port = 3306;
$dbName = "myapp";
$charset = "utf8mb4";

// 构建连接字符串
$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset={$charset}";

最后一条语句的分号省略

PHP 结束标签 ?> 隐含了一个分号和一个换行符。因此,PHP 代码段中的最后一条语句如果紧接 ?>,可以省略分号:

php
<?php
declare(strict_types=1);

// 正确:最后一条语句省略分号(因为有 ?> 结束标签)
echo "Hello World" ?>

// 等价于
<?php echo "Hello World"; ?>
php
<?php
declare(strict_types=1);

// 也正确:中间语句有分号,最后一条省略
$a = 1;
$b = 2;
echo $a + $b ?>

注意事项

省略分号仅适用于 PHP 代码段的最后一条语句,并且仅当紧接 ?> 标签时才有效。如果后面还有新行或其他代码,分号不能省略。

非末尾语句不能省略分号

php
<?php
declare(strict_types=1);

// 错误:非末尾语句省略分号会导致解析错误
// $a = 1
// $b = 2  // Parse error

// 正确:所有非末尾语句都必须有分号
$a = 1;
$b = 2;
$c = $a + $b;

详细说明

结束标签隐式行为的深入理解

结束标签 ?> 的隐式行为包括两个部分:隐含分号和吃掉紧跟的一个换行符。

php
<?php
declare(strict_types=1);

// 结束标签隐含分号
echo "Hello" ?>
World

// 输出:HelloWorld(注意:换行符被吃掉了)
php
<?php
declare(strict_types=1);

// 对比:如果结束标签后有换行
echo "Line 1"; ?>
Line 2

// 输出:Line 1Line 2
// ?> 后的第一个换行被吃掉,但 "Line 2" 前面的换行在 echo 的字符串中
php
<?php
declare(strict_types=1);

// 多个 PHP 段的情况
echo "A"; ?>B<?= "\n" ?>C

// 输出:
// AB
// C
// 解释:echo "A" 输出 A; ?> 隐含分号后输出 B;
// 然后换行符被 ?> 吃掉; <?= "\n" ?> 输出换行; C 被原样输出

语句块与分号

PHP 中的语句块(如函数体、循环体、条件分支)使用花括号 {} 包围。块内的语句仍然需要分号,但花括号本身不需要分号:

php
<?php
declare(strict_types=1);

// 语句块示例
function calculateTotal(int $price, int $quantity): int
{
    $subtotal = $price * $quantity;  // 需要分号
    $tax = (int)($subtotal * 0.1);   // 需要分号
    return $subtotal + $tax;        // 需要分号
}  // 花括号后不需要分号
php
<?php
declare(strict_types=1);

// if 语句块
$score = 85;
if ($score >= 90) {
    $grade = "A";
} elseif ($score >= 80) {
    $grade = "B";
} elseif ($score >= 70) {
    $grade = "C";
} else {
    $grade = "D";
}

// 循环语句块
for ($i = 0; $i < 10; $i++) {
    // 循环体中的语句需要分号
    echo $i;
    echo ", ";
}

控制结构的替代语法与分号

当使用替代语法(如 endifendforeach)时,结束关键字代替了右花括号,其后也不需要分号:

php
<?php
declare(strict_types=1);

$users = ["Alice", "Bob", "Charlie"];

// 替代语法中的分号处理
foreach ($users as $user): ?>
    <p><?= htmlspecialchars($user) ?></p>
<?php endforeach; ?>

// 注意:endforeach 后面没有分号
// 这是因为 endforeach 代替了 },而 } 后面本来也不需要分号

一行多语句

PHP 允许在同一行中写多条语句,用分号分隔:

php
<?php
declare(strict_types=1);

// 一行多条语句(虽然可读性较差)
$a = 10; $b = 20; $c = $a + $b; echo $c;

// 这种写法虽然合法,但不推荐(违反 PSR-12 规范)
// 推荐每条语句独占一行
$a = 10;
$b = 20;
$c = $a + $b;
echo $c;

for 循环中的分号

for 循环有三个表达式,它们之间用分号分隔(不是逗号):

php
<?php
declare(strict_types=1);

// for 循环:分号分隔三个表达式
for ($i = 0; $i < 10; $i++) {
    echo $i . " ";
}

// 第一个分号分隔初始化和条件
// 第二个分号分隔条件和递增

空语句

只有一个分号的空语句是合法的,虽然没有实际效果:

php
declare(strict_types=1);

// 空语句(合法但无实际用途)
;

// 在某些场景下可能有意义,例如 for 循环中的空操作
for ($i = 0; $i < 1000000; $i++) {
    ; // 空循环体(仅用于消耗时间,实际不推荐)
}

实战示例

数据库查询示例

php
<?php
declare(strict_types=1);

// 一个包含多条语句的函数
function fetchUsers(string $status): array
{
    $pdo = new PDO("mysql:host=localhost;dbname=app", "root", "");
    $stmt = $pdo->prepare("SELECT id, name, email FROM users WHERE status = :status");
    $stmt->execute(["status" => $status]);
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return $results;
}

// 调用函数
$activeUsers = fetchUsers("active");
$pendingUsers = fetchUsers("pending");

// 遍历结果(注意每条语句都有分号)
foreach ($activeUsers as $user) {
    $id = $user["id"];
    $name = $user["name"];
    echo "ID: {$id}, Name: {$name}\n";
}

配置文件示例

php
<?php
declare(strict_types=1);

// 配置文件中多条定义语句
return [
    "database" => [
        "host" => getenv("DB_HOST") ?: "localhost",
        "port" => (int)(getenv("DB_PORT") ?: 3306),
        "name" => getenv("DB_NAME") ?: "myapp",
        "user" => getenv("DB_USER") ?: "root",
        "pass" => getenv("DB_PASS") ?: "",
    ],
    "cache" => [
        "driver" => getenv("CACHE_DRIVER") ?: "file",
        "prefix" => getenv("CACHE_PREFIX") ?: "app_",
        "ttl" => (int)(getenv("CACHE_TTL") ?: 3600),
    ],
    "debug" => getenv("APP_DEBUG") === "true",
];

使用输出缓冲的模板处理

php
<?php
declare(strict_types=1);

function render(string $template, array $data = []): string
{
    if (!file_exists($template)) {
        throw new RuntimeException("Template not found: {$template}");
    }
    extract($data, EXTR_SKIP);
    ob_start();
    include $template;
    return ob_get_clean();
}

// 使用渲染函数
try {
    $output = render(__DIR__ . "/templates/home.php", [
        "title" => "首页",
        "content" => "欢迎来到我的网站",
    ]);
    echo $output;
} catch (RuntimeException $e) {
    echo "Error: " . $e->getMessage();
}

注意事项

1. 遗漏分号的常见错误

遗漏分号是最常见的 PHP 语法错误之一:

php
<?php
declare(strict_types=1);

// 错误示例
$x = 10
$y = 20  // Parse error: syntax error, unexpected '$y'

// 正确
$x = 10;
$y = 20;

2. include 文件末尾的分号

当使用 includerequire 时,被包含文件末尾不应有结束标签(遵循 PSR-12),因此最后一条语句的分号是必需的:

php
<?php
declare(strict_types=1);

// config.php - 纯 PHP 文件,不使用结束标签
return [
    "app_name" => "MyApp",
    "version" => "1.0.0",
    // 注意 return 语句后有分号
];
// 文件末尾没有 ?> 标签,因此分号是必需的

3. 不要在花括号后加分号

php
<?php
declare(strict_types=1);

// 错误
// function foo() {};  // 多余的分号

// 正确
function foo(): void
{
    echo "Hello";
}

4. 逗号 vs 分号

在不同上下文中,逗号和分号有不同的用途,不要混淆:

php
<?php
declare(strict_types=1);

// 参数列表用逗号
function add(int $a, int $b): int
{
    return $a + $b;
}

// 数组元素用逗号
$fruits = ["apple", "banana", "orange"];

// 语句用分号
$result = add(1, 2);
echo $result;

// for 循环中:两个分号和一个逗号
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
    echo "i={$i}, j={$j}\n";
}

5. 多行语句的分号位置

PHP 语句可以跨越多行,分号放在最后:

php
<?php
declare(strict_types=1);

// 多行语句,分号在最后
$query = $pdo->prepare(
    "SELECT u.id, u.name, r.role_name"
    . " FROM users u"
    . " LEFT JOIN roles r ON u.role_id = r.id"
    . " WHERE u.is_active = :active"
    . " ORDER BY u.name ASC"
);
$query->execute(["active" => true]);

最佳实践

  1. 始终使用分号:即使最后一条语句紧接 ?>,也建议写上分号,保持一致性
  2. 省略 PHP 结束标签:纯 PHP 文件省略 ?>,此时分号是必需的
  3. 一行一条语句:遵循 PSR-12 规范,每条语句独占一行
  4. 花括号后不加空分号:函数、类、控制结构的花括号后不需要多余的分号
  5. 使用编辑器自动补全:配置 IDE 自动在行尾添加分号,减少遗漏
  6. 区分逗号和分号的使用场景:参数列表和数组元素用逗号,语句用分号
  7. 长语句适当折行:提高可读性,分号放在语句逻辑结束的位置

下一节

理解了指令分隔符的规则后,接下来将学习 PHP 中的注释语法,包括单行注释、多行注释和 phpDoc 文档注释。请阅读 注释

参考链接