PHP 類型約束經歷了三個階段:無類型 → 類/接口/數組/可調用類型提示 (PHP 5) → 標量類型聲明 (PHP 7) → 聯合類型、混合類型 (PHP 8)。本文系統梳理類型約束機制與 SPL Types 擴展方案。
<?php
class Test {
// 支持的類型:類名、接口名、array、callable
public function testArray(array $arr) { print_r($arr); }
public function testClass(Test1 $obj = null) { print_r($obj); }
public function testCallable(callable $cb, $data) { call_user_func($cb, $data); }
public function testInterface(Traversable $it) { echo get_class($it); }
// PHP 7.0+ 標量類型聲明
public function testScalar(int $id, string $name, float $price, bool $active) {}
// PHP 7.0+ 返回類型聲明
public function getName(): string { return 'name'; }
public function getUser(): ?User { return null; } // 可空類型
}
class Test1 {}
$t = new Test();
$t->testArray([1,2,3]);
$t->testClass(new Test1());
$t->testCallable('var_dump', 'hello');
$t->testInterface(new ArrayObject());
?>
| 模式 | 聲明 | 行為 | 適用場景 |
|---|---|---|---|
| 弱類型 (默認) | function foo(int $a) | 自動轉換:foo("123") → int(123) | 一般應用,兼容性好 |
| 嚴格模式 | declare(strict_types=1); function foo(int $a) | 嚴格匹配:foo("123") → TypeError | 核心庫、關鍵業務、API 契約 |
<?php
// 嚴格模式必須放在文件第一行
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
add(1, 2); // 3
add(1.5, 2.5); // TypeError: float 傳給 int
add("1", "2"); // TypeError: string 傳給 int
?>
PECL 擴展 spl_types 將標量封裝為對象,實現運行時類型強制:
<?php
// 安裝: pecl install SPL_Types
$int = new SplInt(94);
echo $int; // 94
try {
$int = 'Try to cast string'; // 嘗試賦值字符串
} catch (UnexpectedValueException $e) {
echo $e->getMessage(); // Value not an integer
}
echo $int; // 94 (原值保持不變)
// 其他類型
$float = new SplFloat(3.14);
$bool = new SplBool(true);
$enum = new SplEnum(['apple', 'banana', 'orange']);
$enum->apple; // 0
$string = new SplString('hello');
?>
<?php
// 聯合類型
function process(int|float $number): string|array|null {}
// 混合類型
function debug(mixed $data): void {}
// 交集類型 (PHP 8.1+)
function serialize(Countable&JsonSerializable $obj): string {}
// never 返回類型 (PHP 8.1+)
function throwException(): never {
throw new Exception();
}
// readonly 屬性 (PHP 8.1+)
class DTO {
public function __construct(
public readonly string $id,
public readonly string $name
) {}
}
?>
declare(strict_types=1);object 或 mixed