字符串语法
概述
PHP 提供了四种定义字符串的方式:单引号、双引号、Heredoc 和 Nowdoc。每种方式有不同的解析规则和适用场景。理解字符串语法的细节,能够帮助开发者编写更高效、更安全的代码。
基础概念
字符串的四种定义方式
| 方式 | 变量解析 | 转义序列 | 适用场景 |
|---|---|---|---|
单引号 ' | 不解析 | 仅 \\ 和 \' | 纯文本、不需要变量 |
双引号 " | 解析 | 完整转义序列 | 包含变量和转义字符 |
Heredoc <<< | 解析 | 完整转义序列 | 多行文本、HTML 模板 |
Nowdoc <<<' | 不解析 | 不处理 | 多行纯文本 |
转义序列速查
| 序列 | 含义 | 单引号 | 双引号 |
|---|---|---|---|
\n | 换行符 | - | LF |
\r | 回车符 | - | CR |
\t | 制表符 | - | TAB |
\v | 垂直制表符 | - | VT |
\e | ESC | - | ESC |
\f | 换页符 | - | FF |
\\ | 反斜杠 | YES | YES |
\' | 单引号 | YES | YES |
\" | 双引号 | YES | YES |
\$ | 美元符号 | - | 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 引擎会扫描字符串中的 $ 符号并尝试解析变量。解析规则如下:
- 简单语法:
$var直接替换变量值,$arr[key]替换数组元素(键不加引号) - 花括号语法:
{$var}、{$arr['key']}、{$obj->prop}使用花括号包裹,更清晰 - 复杂表达式:
{$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;
}注意事项
常见陷阱
- 单引号中的反斜杠:只有
\\和\'需要转义。\$在单引号中不会被解析,会原样输出\$。 - 数组键名歧义:双引号中
$arr[key]的key会被当作常量名处理(已弃用的行为),应使用{$arr['key']}。 - Heredoc 结束标记:结束标记必须独占一行,前面不能有其他字符(PHP 7.3 前不能有缩进)。
安全提醒
在构建 HTML 和 SQL 时,务必对变量进行转义。不要直接将用户输入嵌入字符串中。
最佳实践
- 纯文本使用单引号:不需要变量解析和转义序列时,使用单引号字符串
- 多行文本使用 Heredoc/Nowdoc:HTML 模板、SQL 语句等多行文本首选 Heredoc
- 变量嵌入使用花括号:
{$var}比$var更清晰,避免歧义 - 避免字符串中复杂逻辑:不要在花括号中写复杂的函数调用,可读性差
- Nowdoc 用于纯文本模板:配置文件、邮件模板等不需要解析的内容使用 Nowdoc
- PHP 7.3+ 使用缩进 Heredoc:利用缩进特性,使代码格式更美观