Skip to content

PHP 类型系统概述

概述

PHP 拥有一套丰富且不断演进的类型系统。作为一门动态类型语言,PHP 在历史上以弱类型检查著称,但从 PHP 7.0 开始引入标量类型声明,到 PHP 8.0 的联合类型,再到 PHP 8.1 的枚举类型和交集类型,PHP 的类型系统正在快速向强类型方向发展。理解 PHP 的类型分类、类型转换规则和类型声明机制,是编写健壮、可维护代码的基础。

前置知识

在阅读本节之前,你需要了解:

  • PHP 的基本语法和变量概念
  • 编译型语言与解释型语言的区别
  • 静态类型与动态类型的基本概念
  • 面向对象编程的基础(类、接口)

基础概念

PHP 类型分类

PHP 的类型系统将所有值分为三大类:标量类型复合类型特殊类型

PHP 类型系统
├── 标量类型(Scalar Types)
│   ├── bool      — 布尔值(true / false)
│   ├── int       — 整数
│   ├── float     — 浮点数
│   └── string    — 字符串

├── 复合类型(Compound Types)
│   ├── array     — 数组
│   ├── object    — 对象
│   ├── callable  — 可调用
│   ├── iterable  — 可迭代
│   └── enum      — 枚举(PHP 8.1+)

├── 特殊类型(Special Types)
│   ├── null      — 空值
│   ├── resource  — 资源(如文件句柄)
│   └── mixed     — 混合类型(PHP 8.0+)

└── 伪类型(Pseudo Types,仅用于文档)
    ├── number    — int | float
    ├── void      — 无返回值
    └── never     — 永不返回(PHP 8.1+)

类型分类一览表

分类类型说明可用于类型声明
标量bool布尔值是(PHP 7.0+)
int整数是(PHP 7.0+)
float浮点数是(PHP 7.0+)
string字符串是(PHP 7.0+)
复合array数组是(PHP 5.1+)
object对象是(PHP 7.2+)
callable可调用是(PHP 5.4+)
iterable可迭代是(PHP 7.1+)
enum枚举是(PHP 8.1+)
特殊null空值是(联合类型中)
resource资源
mixed混合类型是(PHP 8.0+)

语法与代码

类型检测函数

php
<?php
declare(strict_types=1);

// is_* 系列函数用于检测变量的类型
$values = [
    true,       // bool
    42,         // int
    3.14,       // float
    'hello',    // string
    [1, 2],     // array
    new stdClass(), // object
    null,       // null
];

foreach ($values as $value) {
    echo match (true) {
        is_bool($value)   => 'bool',
        is_int($value)    => 'int',
        is_float($value)  => 'float',
        is_string($value) => 'string',
        is_array($value)  => 'array',
        is_object($value) => 'object',
        is_null($value)   => 'null',
        default           => 'unknown',
    } . PHP_EOL;
}

// gettype() 返回类型名称字符串
echo gettype(42);     // "integer"
echo gettype(3.14);   // "double"(注意:不是 "float")
echo gettype('hi');   // "string"
echo gettype(null);   // "NULL"

get_debug_type()(PHP 8.0+)

php
<?php
declare(strict_types=1);

// get_debug_type() 返回更精确的类型名称(推荐用于调试)
echo get_debug_type(42);          // "int"
echo get_debug_type(3.14);        // "float"(比 gettype 更准确)
echo get_debug_type('hello');     // "string"
echo get_debug_type(null);        // "null"
echo get_debug_type([1, 2]);      // "array"
echo get_debug_type(new stdClass()); // "stdClass"

// get_debug_type() 对象返回实际的类名
class User {}
echo get_debug_type(new User());  // "User"

// gettype() 对象返回 "object"
echo gettype(new User());         // "object"

自动类型转换(隐式转换)

php
<?php
declare(strict_types=1);

// PHP 会根据上下文自动转换类型
// 这称为"类型杂耍"(Type Juggling)

// 字符串 + 整数
$result = "5" + 3;        // 8(字符串被转为整数)

// 布尔值在算术运算中
$result = true + 1;       // 2(true → 1)
$result = false + 1;      // 1(false → 0)

// 字符串拼接中的整数
$result = "hello" . 42;   // "hello42"(整数被转为字符串)

// 数组与标量比较
var_dump([] == false);    // bool(true)
var_dump([] == null);     // bool(true)
var_dump("" == false);    // bool(true)
var_dump("0" == false);   // bool(true)
var_dump("0" == null);    // bool(false)

// 严格比较(===)不会进行类型转换
var_dump("0" === false);  // bool(false)
var_dump("" === false);   // bool(false)
var_dump("0" === 0);      // bool(false)

类型转换表

PHP 隐式转换遵循以下规则:

源类型 → 目标类型truefalse01"0""""php"[][1]
booltruefalsefalsetruefalsefalsetruefalsetrue
int100100001
float1.00.00.01.00.00.00.00.01.0
string"1""""0""1""0""""php"""*"Array"*

数组转字符串

数组转换为字符串时会产生警告(PHP 8.0+)或返回 "Array"(PHP 7.x)。对象转换为字符串需要实现 __toString() 方法,否则产生致命错误。

详细说明

强类型 vs 弱类型

PHP 支持两种类型检查模式:

模式声明方式类型转换严格比较
弱类型(默认)declare(strict_types=1)自动转换不要求
强类型declare(strict_types=1)不转换,TypeError要求
php
<?php
// 弱类型模式(默认)
function add(int $a, int $b): int
{
    return $a + $b;
}
add("3", "5"); // 正常工作,"3" 和 "5" 被自动转为 int

// 强类型模式
declare(strict_types=1);
function addStrict(int $a, int $b): int
{
    return $a + $b;
}
addStrict("3", "5"); // TypeError: 参数必须为 int,string 被传入

strict_types 的作用范围

declare(strict_types=1) 只影响它所在的文件。如果文件 A 调用文件 B 中定义的函数,即使 B 使用了 strict_types=1,A 中的调用仍然按照 A 的设置处理。strict_types 只影响声明它的文件中函数调用的行为。

类型声明的发展历程

PHP 版本新增类型特性
PHP 5.1数组类型声明(array
PHP 5.4callable 类型声明
PHP 7.0标量类型声明(intfloatstringbool
PHP 7.1void 返回类型、iterable 类型
PHP 7.4属性属性化类型(Typed Properties)
PHP 8.0联合类型(int|string)、mixed 类型、static 返回类型
PHP 8.1枚举类型(enum)、交集类型(A&B)、never 返回类型、只读属性
PHP 8.2DNF 类型((A&B)|C)、true 类型
PHP 8.3readonly 类、#[\Override] 属性

PHP 8.x 类型系统增强

联合类型(PHP 8.0):

php
<?php
declare(strict_types=1);

function processId(int|string $id): string
{
    return is_int($id) ? "ID: #{$id}" : "ID: {$id}";
}
processId(42);     // "ID: #42"
processId("abc");  // "ID: abc"

枚举类型(PHP 8.1):

php
<?php
declare(strict_types=1);

enum Status: string
{
    case Active = 'active';
    case Inactive = 'inactive';
    case Pending = 'pending';
}

function setStatus(Status $status): void
{
    echo "状态: {$status->value}";
}
setStatus(Status::Active); // "状态: active"

实战示例

类型安全的值对象

php
<?php
declare(strict_types=1);

/**
 * 使用 PHP 8.1+ 特性构建类型安全的值对象
 */
readonly class Money
{
    public function __construct(
        public readonly int $amount,
        public readonly string $currency = 'CNY',
    ) {
        if ($amount < 0) {
            throw new InvalidArgumentException('金额不能为负数');
        }
        if (strlen($currency) !== 3) {
            throw new InvalidArgumentException('货币代码必须为 3 个字符');
        }
    }

    public function add(Money $other): Money
    {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException('不能相加不同币种的金额');
        }
        return new Money($this->amount + $other->amount, $this->currency);
    }

    public function format(): string
    {
        return number_format($this->amount / 100, 2) . ' ' . $this->currency;
    }
}

$price = new Money(1999);       // 19.99 CNY
$discount = new Money(500);     // 5.00 CNY
$total = $price->add($discount);
echo $total->format(); // "24.99 CNY"

类型验证工具

php
<?php
declare(strict_types=1);

/**
 * 通用的类型验证和转换工具
 */
class TypeChecker
{
    /**
     * 验证值是否匹配预期的联合类型
     */
    public static function matches(mixed $value, string $expectedType): bool
    {
        $types = array_map('trim', explode('|', $expectedType));

        foreach ($types as $type) {
            if (self::checkSingleType($value, $type)) {
                return true;
            }
        }
        return false;
    }

    private static function checkSingleType(mixed $value, string $type): bool
    {
        return match ($type) {
            'int', 'integer'     => is_int($value),
            'float', 'double'   => is_float($value),
            'string'             => is_string($value),
            'bool', 'boolean'    => is_bool($value),
            'array'              => is_array($value),
            'object'             => is_object($value),
            'null'               => is_null($value),
            'callable'           => is_callable($value),
            'iterable'           => is_iterable($value),
            'mixed'              => true,
            'void', 'never'      => $value === null,
            default              => $value instanceof $type,
        };
    }

    /**
     * 获取值的确切类型(人类可读)
     */
    public static function getTypeName(mixed $value): string
    {
        return get_debug_type($value);
    }
}

// 使用示例
var_dump(TypeChecker::matches(42, 'int|string'));        // true
var_dump(TypeChecker::matches('hello', 'int|string'));    // true
var_dump(TypeChecker::matches(3.14, 'int|string'));     // false
var_dump(TypeChecker::matches(null, 'null|string'));    // true

注意事项

1. gettype() 与 get_debug_type() 的差异

gettype() 返回的历史名称(如 double 而非 floatinteger 而非 int)与现代类型声明不匹配。PHP 8.0+ 推荐使用 get_debug_type()

2. == 与 === 的区别

== 在比较前会进行类型转换,而 === 要求类型和值都相同。在类型敏感的场景中,始终使用 ===

3. 伪类型的限制

voidnevernumber 等伪类型只能用于类型声明,不能用于 gettype() 或变量操作。

4. 类型声明的文件作用域

declare(strict_types=1) 必须在文件的第一行<?php 标签之后),且只影响当前文件。

最佳实践

  1. 启用严格模式:在所有新文件中使用 declare(strict_types=1)
  2. 使用具体类型:优先使用具体的类型声明,避免滥用 mixed
  3. 使用 === 比较:避免 == 的隐式类型转换
  4. 使用 get_debug_type():替代 gettype() 进行调试
  5. 联合类型优于 mixed:当接受多种类型时使用联合类型声明
  6. 只读属性:PHP 8.1+ 使用 readonly 保护不可变数据

下一节

下一节将详细介绍 PHP 的类型声明与严格模式,了解 declare(strict_types=1) 的工作原理和类型声明的各种用法。

进阶用法

调试与测试技巧

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

参考链接