PHP项目类型系统与严格模式

wen PHP项目 2

PHP 类型系统与严格模式

PHP 的类型系统经历过重大演变,从动态类型逐渐增加了静态类型特性,严格模式是这一演进中的关键特性。

PHP项目类型系统与严格模式

类型系统基础

弱类型(默认模式)

// PHP 默认是弱类型,会自动进行类型转换
function add($a, $b) {
    return $a + $b;
}
echo add("5", 3);    // 8 - 字符串自动转为整数
echo add("5 apples", 3); // 8 - "5 apples" 转为 5
echo add("hello", 3);    // 3 - "hello" 转为 0

类型声明(PHP 7+)

// 参数类型声明
function calculate(int $a, float $b): float {
    return $a * $b;
}
// 默认模式下仍会尝试类型转换
echo calculate("5", 2.5); // 12.5 - "5" 转为整数 5

严格模式

启用严格模式

<?php
declare(strict_types=1);
// 现在类型必须严格匹配
function add(int $a, int $b): int {
    return $a + $b;
}
// add("5", 3); // TypeError: 参数 1 必须是整数,字符串给定
add(5, 3); // 8 - 正确

严格模式的特性

  • 作用域:基于文件级别,每个文件需要单独声明
  • 继承影响:子类方法必须与父类保持相同的严格性
  • 类型检查:不会进行隐式类型转换

支持的类型声明

declare(strict_types=1);
// 基本类型
function basicTypes(
    int $int,
    float $float,
    string $string,
    bool $bool,
    array $array,
    callable $callable
): void {}
// 特殊类型
function specialTypes(
    mixed $mixed,        // PHP 8.0+ - 任意类型
    never $never,        // PHP 8.1+ - 永不返回
    void $void,          // 无返回值
    null $null,          // 仅允许 null
    false $false,        // PHP 8.0+ - 仅允许 false
    true $true,          // PHP 8.2+ - 仅允许 true
): void {}
// 对象类型
function objectTypes(
    stdClass $object,    // 特定类名
    object $objectType,  // PHP 7.2+ - 任何对象
    iterable $iterable,  // PHP 7.1+ - 数组或 Traversable
): void {}

联合类型与交叉类型

declare(strict_types=1);
// 联合类型 (PHP 8.0+)
function processInput(int|string|array $input): void {
    if (is_int($input)) {
        echo "整数: $input";
    } elseif (is_string($input)) {
        echo "字符串: $input";
    } else {
        echo "数组";
    }
}
// 可空联合类型
function findUser(int|null $id): ?string {
    if ($id === null) {
        return null;
    }
    return "User #$id";
}
// 交叉类型 (PHP 8.1+)
interface Loggable {
    public function log(): void;
}
interface Serializable {
    public function serialize(): string;
}
function processLoggableSerializable(Loggable&Serializable $item): void {
    $item->log();
    echo $item->serialize();
}

伪类型与内置类型

declare(strict_types=1);
// mixed - PHP 8.0+
function mixedExample(mixed $data): mixed {
    return match(true) {
        is_string($data) => strtoupper($data),
        is_int($data) => $data * 2,
        default => null
    };
}
// never - 永远不会返回值
function throwException(string $message): never {
    throw new \RuntimeException($message);
}
// void
function logMessage(string $message): void {
    echo "[LOG] $message" . PHP_EOL;
    // 不能有 return 语句(除非 return; 没有值)
}

类型推断与类型系统特性

declare(strict_types=1);
// 属性类型声明 (PHP 7.4+)
class User {
    public string $name;
    protected int $age;
    private array $roles;
    public ?DateTimeImmutable $createdAt = null;
    // 只读属性 (PHP 8.1+)
    public readonly string $email;
    public function __construct(
        string $name,
        int $age,
        string $email
    ) {
        $this->name = $name;
        $this->age = $age;
        $this->email = $email;
    }
}
// 返回类型推断
class Calculator {
    public function add(int $a, int $b): static {  // PHP 8.0+
        $result = new static();
        $result->value = $a + $b;
        return $result;
    }
    private int $value = 0;
}
// 泛型风格 (通过 PHPDoc)
/** @template T */
class Collection {
    /** @var array<int, T> */
    private array $items = [];
    /** @param T $item */
    public function add(mixed $item): void {
        $this->items[] = $item;
    }
    /** @return T */
    public function get(int $index): mixed {
        return $this->items[$index] ?? null;
    }
}
// 使用方式
$users = new Collection();
$users->add(new User('Alice', 30, 'alice@example.com'));
$user = $users->get(0); // User|null

类型转换与注意事项

declare(strict_types=1);
// 安全的类型转换
function safeConversion(): void {
    // 显式转换
    $intValue = (int) "123";      // 123
    $floatValue = (float) "3.14"; // 3.14
    $stringValue = (string) 123;  // "123"
    $boolValue = (bool) 1;        // true
    // 类型转换函数
    $intval = intval("456");      // 456
    $floatval = floatval("7.89"); // 7.89
    $strval = strval(true);       // "1"
}
// 严格模式下的常见陷阱
function strictTraps(): void {
    // 1. 除法总是返回 float 或 int
    $result = 10 / 3;    // 3.3333333333333 (float)
    $result = 10 / 5;    // 2.0 (float)
    $result = intdiv(10, 3); // 3 (整数除法)
    // 2. 字符串连接
    $message = "Count: " . 5; // 总是字符串
}
// 类型判断
function typeChecking(mixed $value): string {
    return match(true) {
        is_int($value) => 'integer',
        is_float($value) => 'float',
        is_string($value) => 'string',
        is_bool($value) => 'boolean',
        is_array($value) => 'array',
        is_object($value) => 'object: ' . get_class($value),
        is_null($value) => 'null',
        is_resource($value) => 'resource',
        default => 'unknown'
    };
}

最佳实践

declare(strict_types=1);
// 1. 始终使用严格模式
<?php
declare(strict_types=1);
// 你的代码...
// 2. 为所有函数和方法的参数和返回值添加类型声明
function createUser(string $name, string $email, int $age): User {
    // 实现
}
// 3. 使用 nullable 类型表示可能为 null 的值
function findById(int $id): ?User {
    // 实现
}
// 4. 使用联合类型增加灵活性
function formatValue(int|float|string $value): string {
    // 实现
}
// 5. 使用 readonly 属性确保不变性
class Config {
    public readonly array $settings;
    public function __construct(array $settings) {
        $this->settings = $settings;
    }
}
// 6. 使用 value object 替代原始类型
class Email {
    public readonly string $value;
    public function __construct(string $email) {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException("Invalid email");
        }
        $this->value = $email;
    }
}

性能考虑

严格模式下的类型检查会带来轻微的性能开销,但通常可以忽略不计,实际收益包括:

  • 更早的错误发现:在开发阶段就能发现问题
  • 更好的 IDE 支持:代码补全和类型提示更准确
  • 自文档化:类型声明本身就是文档
  • 更可靠的代码:减少类型相关的 bug

PHP 的类型系统已经从弱类型演变为具有丰富类型特性的语言,严格模式是 PHP 现代开发中的最佳实践,能显著提高代码质量和可维护性,建议在新项目中始终启用严格模式,并充分利用 PHP 提供的类型系统特性。

抱歉,评论功能暂时关闭!