Skip to content

字符串运算符

概述

PHP 提供了两个专门的字符串运算符:连接运算符 .(拼接)和连接赋值运算符 .=(追加)。此外,PHP 还支持通过字符串插值在双引号字符串、heredoc 和 nowdoc 中嵌入变量。了解不同字符串拼接方式的性能差异有助于编写高效代码。

PHP 8.0 优先级变更

PHP 8.0 起字符串连接符 . 的优先级低于 +-<<>>。在此之前 .+/- 优先级相同。

基础概念

运算符一览

运算符名称示例等价写法
.字符串拼接$a . $b将两个字符串连接
.=字符串追加$a .= $b$a = $a . $b

类型转换

. 的操作数不是字符串时,PHP 会自动将其转换为字符串。整数、浮点数、布尔值和 null 都会按规则转换。数组和对象转换为字符串会产生 Notice/Warning。

语法与代码示例

基本字符串拼接

php
<?php

declare(strict_types=1);

// 基本拼接
$firstName = "Alice";
$lastName = "Smith";
$fullName = $firstName . " " . $lastName;
echo $fullName . "\n"; // Alice Smith

// 非字符串类型的拼接
$age = 30;
$greeting = "Name: " . $firstName . ", Age: " . $age;
echo $greeting . "\n"; // Name: Alice, Age: 30

// null 拼接为空字符串
$value = null;
echo "Value: " . $value . "\n"; // Value: (空)

// 布尔值拼接
$isActive = true;
echo "Active: " . $isActive . "\n"; // Active: 1
$inactive = false;
echo "Inactive: " . $inactive . "\n"; // Inactive: (空)

字符串追加 .=

php
<?php

declare(strict_types=1);

// 逐步构建字符串
$html = '<!DOCTYPE html>';
$html .= '<html lang="en">';
$html .= '<head><title>My Page</title></head>';
$html .= '<body>';
$html .= '<h1>Hello World</h1>';
$html .= '</body></html>';

// 构建 SQL 查询
$query = "SELECT id, name, email FROM users";
$query .= " WHERE status = 'active'";
$query .= " AND role IN ('admin', 'editor')";
$query .= " ORDER BY created_at DESC";
$query .= " LIMIT 10";
echo $query . "\n";

// 构建 JSON 字符串
$json = '{';
$json .= '"name": "Alice",';
$json .= '"age": 30,';
$json .= '"email": "alice@example.com"';
$json .= '}';
echo $json . "\n";

字符串插值

php
<?php

declare(strict_types=1);

$name = "Alice";
$age = 30;
$city = "Shanghai";

// 双引号中的简单变量插值
echo "Hello, {$name}! You are {$age} years old.\n";

// 复杂表达式插值(使用花括号)
echo "In 5 years you will be {$age + 5}.\n";
echo "Hello, {strtoupper($name)}.\n";

// 数组元素插值
$data = ['name' => 'Alice', 'score' => 95];
echo "Student: {$data['name']}, Score: {$data['score']}\n";

// 对象属性插值
class User
{
    public function __construct(
        public string $name = 'Alice',
        public int $age = 30
    ) {}
}

$user = new User();
echo "User: {$user->name}, Age: {$user->age}\n";

// 变量变量插值
$varName = 'name';
echo "Variable: ${$varName}\n"; // Alice

Heredoc 中的变量解析

php
<?php

declare(strict_types=1);

$name = "Alice";
$role = "Admin";

// Heredoc:支持变量插值
$html = <<<HTML
<!DOCTYPE html>
<html>
<head><title>Dashboard</title></head>
<body>
    <h1>Welcome, {$name}</h1>
    <p>Role: {$role}</p>
    <p>Date: {date('Y-m-d')}</p>
</body>
</html>
HTML;

echo $html . "\n";

// Nowdoc:不支持变量解析(单引号语义)
$static = <<<'TEXT'
<!DOCTYPE html>
<html>
<head><title>Static Page</title></head>
<body>
    <h1>Hello, {$name}</h1>
    <!-- {$name} 不会被替换,原样输出 -->
</body>
</html>
TEXT;

echo $static . "\n";

// PHP 7.3+ Heredoc 结束标记可以缩进
// PHP 7.3+ Heredoc 可以用作函数参数
function renderTemplate(string $content): string
{
    return $content;
}

$result = renderTemplate(<<<TEMPLATE
    <div class="card">
        <h2>{$name}</h2>
        <p>Role: {$role}</p>
    </div>
    TEMPLATE);

详细说明

字符串插值的两种形式

php
<?php

declare(strict_types=1);

$items = ['apple', 'banana', 'cherry'];
$count = 3;

// 简单插值:变量后直接跟合法字符时无需花括号
echo "Count: $count\n";        // 正确
echo "Item: $items[0]\n";      // 正确:简单数组索引

// 注意:不加花括号时,变量名解析可能出错
$obj = new stdClass();
$obj->name = "Alice";
echo "Name: $obj->name\n";     // 正确

// 复杂表达式必须使用花括号
echo "Total: {$count * 10}\n";
echo "Upper: {strtoupper('hello')}\n";
echo "Nested: {$items[$count - 1]}\n";

// 花括号外有歧义时必须使用花括号
$prefix = "user";
echo "{$prefix}_id\n";         // user_id
echo "$prefix_id\n";           // 变量 $prefix_id(可能报 Notice)

拼接与插值的性能对比

php
<?php

declare(strict_types=1);

// 拼接方式
$benchTimes = 100000;

// 方式1:点号拼接
$start = microtime(true);
for ($i = 0; $i < $benchTimes; $i++) {
    $str = 'Hello' . ', ' . 'World' . '!';
}
$concatTime = microtime(true) - $start;

// 方式2:双引号插值
$name = 'World';
$start = microtime(true);
for ($i = 0; $i < $benchTimes; $i++) {
    $str = "Hello, {$name}!";
}
$interpTime = microtime(true) - $start;

// 方式3:sprintf
$start = microtime(true);
for ($i = 0; $i < $benchTimes; $i++) {
    $str = sprintf('Hello, %s!', $name);
}
$sprintfTime = microtime(true) - $start;

// 方式4:数组 + implode
$start = microtime(true);
for ($i = 0; $i < $benchTimes; $i++) {
    $str = implode('', ['Hello', ', ', $name, '!']);
}
$implodeTime = microtime(true) - $start;

echo "Concat: {$concatTime}s\n";
echo "Interp:  {$interpTime}s\n";
echo "Sprintf: {$sprintfTime}s\n";
echo "Implode: {$implodeTime}s\n";
// 性能差异通常很小,但 sprintf 可读性最好(复杂格式)

性能建议

对于少量拼接,性能差异可以忽略。对于大量拼接(如循环中构建大字符串),建议使用数组收集 + implode()方式,或使用 sprintf() 提高可读性。

实战示例

构建 HTML 模板

php
<?php

declare(strict_types=1);

class HtmlBuilder
{
    private string $html = '';

    public function addElement(string $tag, string $content, array $attrs = []): self
    {
        $attrStr = '';
        foreach ($attrs as $key => $value) {
            $attrStr .= sprintf(' %s="%s"', htmlspecialchars($key), htmlspecialchars($value));
        }
        $this->html .= "<{$tag}{$attrStr}>{$content}</{$tag}>";
        return $this;
    }

    public function addRaw(string $html): self
    {
        $this->html .= $html;
        return $this;
    }

    public function build(): string
    {
        return $this->html;
    }
}

$page = (new HtmlBuilder())
    ->addElement('h1', 'User List', ['class' => 'title'])
    ->addElement('p', 'Total users: 42')
    ->addElement('ul', '', ['id' => 'user-list'])
    ->build();

echo $page . "\n";

SQL 查询构建器

php
<?php

declare(strict_types=1);

class QueryBuilder
{
    private array $conditions = [];
    private array $params = [];
    private array $orderBy = [];
    private ?int $limit = null;

    public function where(string $condition, mixed $param = null): self
    {
        $this->conditions[] = $condition;
        if ($param !== null) {
            $this->params[] = $param;
        }
        return $this;
    }

    public function orderBy(string $column, string $direction = 'ASC'): self
    {
        $this->orderBy[] = "{$column} {$direction}";
        return $this;
    }

    public function limit(int $count): self
    {
        $this->limit = $count;
        return $this;
    }

    public function build(string $table): string
    {
        $sql = "SELECT * FROM {$table}";

        if (!empty($this->conditions)) {
            $sql .= " WHERE " . implode(" AND ", $this->conditions);
        }

        if (!empty($this->orderBy)) {
            $sql .= " ORDER BY " . implode(", ", $this->orderBy);
        }

        if ($this->limit !== null) {
            $sql .= " LIMIT {$this->limit}";
        }

        return $sql;
    }
}

$query = (new QueryBuilder())
    ->where('status = ?', 'active')
    ->where('role IN (?, ?)', ['admin', 'editor'])
    ->orderBy('created_at', 'DESC')
    ->limit(10)
    ->build('users');

echo $query . "\n";

注意事项

  • PHP 8.0 中 . 优先级变更"str" . $x - 1 在 PHP 8 中会抛出 TypeError
  • 数组拼接$arr . "" 在 PHP 8.0+ 中产生 Warning(之前是 Notice)
  • 资源类型拼接$fp . "" 会产生 Notice
  • Heredoc 缩进:PHP 7.3+ 支持 Heredoc 结束标记缩进,缩进部分会从内容中移除
  • sprintf 格式化:复杂格式化建议使用 sprintf,可读性优于字符串拼接

最佳实践

  1. 少量变量用双引号插值"Hello, {$name}" 优于 'Hello, ' . $name
  2. 复杂构建用 implode:循环中拼接大量字符串时,先收集到数组再 implode
  3. SQL 查询用参数化:不要用字符串拼接 SQL,使用 PDO 预处理语句
  4. HTML 属性转义:拼接 HTML 时始终使用 htmlspecialchars()
  5. 大段静态文本用 Heredoc/Nowdoc:避免转义引号的麻烦

进阶用法

调试与测试技巧

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');

参考链接