Skip to content

字符串语法

概述

PHP 提供了四种定义字符串的方式:单引号、双引号、Heredoc 和 Nowdoc。每种方式有不同的解析规则和适用场景。理解字符串语法的细节,能够帮助开发者编写更高效、更安全的代码。

基础概念

字符串的四种定义方式

方式变量解析转义序列适用场景
单引号 '不解析\\\'纯文本、不需要变量
双引号 "解析完整转义序列包含变量和转义字符
Heredoc <<<解析完整转义序列多行文本、HTML 模板
Nowdoc <<<'不解析不处理多行纯文本

转义序列速查

序列含义单引号双引号
\n换行符-LF
\r回车符-CR
\t制表符-TAB
\v垂直制表符-VT
\eESC-ESC
\f换页符-FF
\\反斜杠YESYES
\'单引号YESYES
\"双引号YESYES
\$美元符号-YES
\x[0-9A-Fa-f]{1,2}十六进制字符-YES
\u{[0-9A-Fa-f]+}Unicode 字符-PHP 7.0+
\0~\777八进制字符-YES

语法与代码

单引号字符串

单引号字符串不会解析其中的变量和大多数转义序列,只有 \\(反斜杠)和 \'(单引号)例外。

php
<?php

declare(strict_types=1);

// 单引号 - 不解析变量和转义序列
$name = 'World';
$greeting = 'Hello, $name';  // 输出: Hello, $name(原样输出)
$path = 'C:\\App\\logs';    // 反斜杠需要转义
$quote = 'It\'s PHP';       // 单引号内使用单引号需转义

// 常见用法:SQL 语句、文件路径、纯文本
$sql = 'SELECT * FROM users WHERE id = 1';
$filePath = '/var/www/html/index.php';

双引号字符串

双引号字符串会解析变量和转义序列,支持多种变量嵌入方式。

php
<?php

declare(strict_types=1);

// 双引号 - 解析变量和转义序列
$name = 'World';
$greeting = "Hello, $name";          // 输出: Hello, World
$lineBreak = "第一行\n第二行";         // 包含换行符
$hex = "\x41\x42\x43";               // 输出: ABC
$unicode = "\u{4F60}\u{597D}";       // PHP 7.0+ 输出: 你好

// 变量解析 - 简单语法
$user = ['name' => 'Alice', 'age' => 30];
echo "姓名: $user[name]";             // 输出: 姓名: Alice(不带引号的键)
echo "姓名: {$user['name']}";         // 输出: 姓名: Alice(花括号语法,推荐)

花括号语法 ${expr} 与变量解析

PHP 支持两种花括号语法来嵌入复杂表达式。

php
<?php

declare(strict_types=1);

// 花括号语法 - 复合变量解析
$user = ['name' => 'Alice', 'age' => 30];
$class = 'User';

// {$...} 语法(推荐,最清晰)
echo "用户: {$user['name']},年龄: {$user['age']}";
echo "类名: {$class}::class";

// ${...} 语法(较早的写法)
echo "用户: ${user['name']}";  // 也可以工作

// 复杂表达式解析
$items = [1, 2, 3];
echo "共 " . count($items) . " 项";              // 拼接方式
echo "共 {count($items)} 项";                      // 双引号内花括号
echo "第一个: {$items[0]},最后一个: {$items[count($items) - 1]}";

// 对象属性和方法调用
$obj = new stdClass();
$obj->name = 'Test';
echo "对象名: {$obj->name}";                      // 属性访问
echo "大写: {strtoupper($obj->name)}";

PHP 8.2+ 变量解析改进

PHP 8.2 对 ${expr} 语法进行了改进,不再要求在双引号中使用花括号包裹变量名。

php
<?php

declare(strict_types=1);

// PHP 8.2+ 新增:更灵活的变量解析
$name = 'Alice';
$items = ['a', 'b', 'c'];

// PHP 8.2 中可以使用更自然的表达式嵌入
echo "你好 {$name}";          // 传统写法仍然有效
echo "你好 ${name}";           // 仍然支持

// PHP 8.2 移除了 ${var} 中对不存在的变量的静默处理
// 以前: "${undefinedVar}" 只输出空字符串
// PHP 8.2+: "${undefinedVar}" 会产生 Warning

// PHP 8.2 改进了嵌套花括号的处理
$deep = ['users' => ['admin' => 'root']];
echo "管理员: {$deep['users']['admin']}";

Heredoc 语法

Heredoc 用于定义多行字符串,内部会解析变量和转义序列。PHP 7.3+ 允许结束标记缩进。

php
<?php

declare(strict_types=1);

// PHP 7.3+ 缩进 Heredoc(推荐)
$html = <<<HTML
    <div class="container">
        <h1>{$title}</h1>
        <p>内容: {$content}</p>
    </div>
    HTML;

// PHP 8.3+ 允许在 Heredoc 中使用类常量
class Config
{
    public const DB_HOST = 'localhost';
}

$sql = <<<SQL
    SELECT * FROM users
    WHERE host = {Config::DB_HOST}
    SQL;

// Heredoc 用于函数参数
$result = someFunction(<<<TEXT
    这是一段多行文本
    可以包含变量: $name
    TEXT);

// 双引号等效
// <<<IDENTIFIER 等同于 "string"
// 注意结束标记必须独占一行(PHP 7.3 前不能缩进)

Nowdoc 语法

Nowdoc 是单引号字符串的多行等价物,不解析任何变量和转义序列。PHP 7.3+ 允许结束标记缩进。

php
<?php

declare(strict_types=1);

// PHP 7.3+ 缩进 Nowdoc
$name = 'Alice';
$template = <<<'TEMPLATE'
    Dear {$name},

    Thank you for joining us.
    Your account has been created.

    Best regards
    TEMPLATE;
// 输出中 {$name} 会原样保留

// Nowdoc 用于 SQL 模板、配置文本等不需要解析的内容
$sql = <<<'SQL'
    CREATE TABLE users (
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(255) NOT NULL,
        email VARCHAR(255) UNIQUE NOT NULL
    )
    SQL;

// Nowdoc 等同于单引号
// <<<'IDENTIFIER' 等同于 'string'

详细说明

变量解析规则

在双引号字符串和 Heredoc 中,PHP 引擎会扫描字符串中的 $ 符号并尝试解析变量。解析规则如下:

  1. 简单语法$var 直接替换变量值,$arr[key] 替换数组元素(键不加引号)
  2. 花括号语法{$var}{$arr['key']}{$obj->prop} 使用花括号包裹,更清晰
  3. 复杂表达式{$func()}{$obj->method()} 在花括号中调用函数或方法

选择建议

优先使用 {$...} 花括号语法,因为它最清晰、最不容易出错。避免使用 $arr[key] 简单语法,因为键名可能与常量混淆。

性能对比

单引号字符串由于不需要解析变量和转义序列,解析速度略快于双引号字符串。但在实际应用中,这个差异几乎可以忽略不计。

php
<?php

declare(strict_types=1);

// 性能差异微乎其微,可读性更重要
$str1 = 'Hello ' . $name;     // 拼接
$str2 = "Hello $name";        // 双引号解析

// 最佳实践:根据场景选择
// 纯文本 -> 单引号
$html = '<div class="box">';

// 含变量 -> 双引号或拼接
$welcome = "欢迎你, {$name}";  // 推荐
$welcome = '欢迎你, ' . $name; // 也可以

// 多行文本 -> Heredoc / Nowdoc
$query = <<<SQL
    SELECT id, name FROM users
    WHERE status = 'active'
    SQL;

PHP 7.3+ Heredoc/Nowdoc 改进

PHP 7.3 对 Heredoc 和 Nowdoc 进行了重大改进:

  • 结束标记可以缩进(不再要求顶格)
  • 结束标记后的内容会被移除(包括换行符)
  • 可以用在函数参数、数组元素等位置
php
<?php

declare(strict_types=1);

// PHP 7.3+: 结束标记可以缩进
$values = [
    'key' => <<<'EOF'
        这是多行文本
        结束标记前的缩进会被保留
        但结束标记本身和之后的换行会被移除
        EOF,
];

// PHP 7.3+: 在函数调用中直接使用
$result = str_replace(
    'search',
    <<<'REPLACE'
        多行替换文本
        支持各种格式
        REPLACE,
    $input
);

实战示例

HTML 模板构建

php
<?php

declare(strict_types=1);

function renderUserCard(array $user): string
{
    $name = htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');
    $email = htmlspecialchars($user['email'], ENT_QUOTES, 'UTF-8');
    $role = htmlspecialchars($user['role'] ?? 'member', ENT_QUOTES, 'UTF-8');

    return <<<HTML
        <div class="user-card">
            <h3>{$name}</h3>
            <p>Email: {$email}</p>
            <span class="badge">{$role}</span>
        </div>
        HTML;
}

$user = ['name' => 'Alice <script>', 'email' => 'alice@example.com', 'role' => 'admin'];
echo renderUserCard($user);

SQL 语句构建

php
<?php

declare(strict_types=1);

function buildSelectQuery(string $table, array $columns, ?string $where = null): string
{
    $cols = implode(', ', $columns);

    if ($where !== null) {
        return <<<SQL
            SELECT {$cols}
            FROM {$table}
            WHERE {$where}
            ORDER BY id DESC
            LIMIT 100
            SQL;
    }

    return <<<SQL
        SELECT {$cols}
        FROM {$table}
        ORDER BY id DESC
        LIMIT 100
        SQL;
}

echo buildSelectQuery('users', ['id', 'name', 'email'], 'status = 1');

JSON 模板(使用 Nowdoc)

php
<?php

declare(strict_types=1);

// 使用 Nowdoc 避免变量被解析
$jsonTemplate = <<<'JSON'
{
    "name": "{{name}}",
    "email": "{{email}}",
    "created_at": "{{timestamp}}"
}
JSON;

// 使用花括号语法构建 JSON(注意安全性)
function toJson(array $data): string
{
    $encoded = array_map(
        fn(string $v): string => addslashes(json_encode($v)),
        $data
    );

    return <<<JSON
    {
        "name": {$encoded['name']},
        "email": {$encoded['email']}
    }
    JSON;
}

注意事项

常见陷阱

  1. 单引号中的反斜杠:只有 \\\' 需要转义。\$ 在单引号中不会被解析,会原样输出 \$
  2. 数组键名歧义:双引号中 $arr[key]key 会被当作常量名处理(已弃用的行为),应使用 {$arr['key']}
  3. Heredoc 结束标记:结束标记必须独占一行,前面不能有其他字符(PHP 7.3 前不能有缩进)。

安全提醒

在构建 HTML 和 SQL 时,务必对变量进行转义。不要直接将用户输入嵌入字符串中。

最佳实践

  1. 纯文本使用单引号:不需要变量解析和转义序列时,使用单引号字符串
  2. 多行文本使用 Heredoc/Nowdoc:HTML 模板、SQL 语句等多行文本首选 Heredoc
  3. 变量嵌入使用花括号{$var}$var 更清晰,避免歧义
  4. 避免字符串中复杂逻辑:不要在花括号中写复杂的函数调用,可读性差
  5. Nowdoc 用于纯文本模板:配置文件、邮件模板等不需要解析的内容使用 Nowdoc
  6. PHP 7.3+ 使用缩进 Heredoc:利用缩进特性,使代码格式更美观

参考链接