名称: PHP 现代特性 用户可调用: false 描述: 使用现代PHP特性,包括类型属性、联合类型、匹配表达式、命名参数、属性、枚举和模式,来编写类型安全、表达性强的PHP代码,利用最新语言改进。 允许工具: []
PHP 现代特性
引言
现代PHP(7.4+、8.0+、8.1+、8.2+)已通过特性显著进化,提高了类型安全性、表达性和开发体验。这些添加将PHP从松散类型的脚本语言转变为构建健壮应用程序的强大、类型安全平台。
关键改进包括严格类型、属性和参数类型、联合和交集类型、匹配表达式、命名参数、属性(注释)、枚举和只读属性。这些特性支持更清晰的意图、更好的IDE支持和更少的运行时错误。
此技能涵盖类型属性、联合/交集类型、匹配表达式、枚举、属性、命名参数和有效利用现代PHP的模式。
类型属性和参数
类型声明通过运行时强制执行类型和启用更好的静态分析来提高代码可靠性。
<?php
// PHP 7.4+ 类型属性
class User {
public string $name;
public int $age;
public ?string $email = null; // 可空
private array $roles = [];
protected bool $active = true;
}
$user = new User();
$user->name = "Alice"; // 正常
$user->age = 30; // 正常
// $user->age = "thirty"; // TypeError
// 构造函数属性提升(PHP 8.0+)
class Product {
public function __construct(
public string $name,
public float $price,
public int $stock,
private ?string $sku = null
) {}
}
$product = new Product("Laptop", 999.99, 10);
echo $product->name; // "Laptop"
// echo $product->sku; // 错误:私有属性
// 返回类型声明
function calculateTotal(float $price, int $quantity): float {
return $price * $quantity;
}
function findUser(int $id): ?User {
// 返回 User 或 null
return $id > 0 ? new User() : null;
}
function getUsers(): array {
return [new User(), new User()];
}
// Void 返回类型
function logMessage(string $message): void {
echo $message . "
";
}
// Never 返回类型(PHP 8.1+)
function fail(string $message): never {
throw new Exception($message);
}
// Mixed 类型(PHP 8.0+)
function process(mixed $value): mixed {
return $value;
}
// Static 返回类型
class Builder {
public function setName(string $name): static {
return $this;
}
public function build(): static {
return new static();
}
}
// 参数类型
function greet(
string $name,
int $age,
bool $formal = false
): string {
$greeting = $formal ? "Good day" : "Hello";
return "$greeting, $name ($age)";
}
// 可变类型参数
function sum(int ...$numbers): int {
return array_sum($numbers);
}
$total = sum(1, 2, 3, 4, 5); // 15
类型属性和参数及早捕获类型错误,并为函数接口提供清晰的契约。
联合和交集类型
联合类型允许多个类型可能性,而交集类型要求同时满足所有指定类型。
<?php
// 联合类型(PHP 8.0+)
function processId(int|string $id): void {
if (is_int($id)) {
echo "处理整数ID: $id
";
} else {
echo "处理字符串ID: $id
";
}
}
processId(123);
processId("ABC-456");
// 联合类型属性
class Response {
public function __construct(
public int|string $code,
public array|string $data
) {}
}
// 可空作为联合类型
function findProduct(int $id): Product|null {
return $id > 0 ? new Product("Item", 10.0, 5) : null;
}
// 多个联合类型
function format(int|float|string $value): string {
return match (true) {
is_int($value) => "整数: $value",
is_float($value) => "浮点数: $value",
is_string($value) => "字符串: $value",
};
}
// 联合类型中的 False 伪类型
function parseValue(string $input): int|false {
$result = filter_var($input, FILTER_VALIDATE_INT);
return $result !== false ? $result : false;
}
// 与内置类型的联合类型
function getData(): array|object|null {
return ['key' => 'value'];
}
// 交集类型(PHP 8.1+)
interface Loggable {
public function log(): void;
}
interface Cacheable {
public function cache(): void;
}
// 接受交集类型的函数
function process(Loggable&Cacheable $object): void {
$object->log();
$object->cache();
}
class Service implements Loggable, Cacheable {
public function log(): void {
echo "日志记录
";
}
public function cache(): void {
echo "缓存
";
}
}
// 与联合的交集
function handle((Loggable&Cacheable)|null $object): void {
$object?->log();
}
// DNF 类型(PHP 8.2+ - 析取范式)
function advanced((Loggable&Cacheable)|(Loggable&Serializable) $object): void {
$object->log();
}
联合类型支持灵活的参数接受,而交集类型同时强制执行多个能力。
匹配表达式
匹配表达式提供模式匹配,具有严格比较和详尽检查,改进了 switch 语句。
<?php
// 基本匹配表达式(PHP 8.0+)
$status = 200;
$message = match ($status) {
200 => "OK",
404 => "Not Found",
500 => "Server Error",
default => "Unknown",
};
echo $message; // "OK"
// 多个条件
$result = match ($status) {
200, 201, 202 => "Success",
400, 401, 403 => "Client Error",
500, 502, 503 => "Server Error",
default => "Unknown",
};
// 带表达式的匹配
$age = 25;
$category = match (true) {
$age < 13 => "Child",
$age < 18 => "Teen",
$age < 65 => "Adult",
default => "Senior",
};
// 无默认的匹配(无匹配时抛出)
function getColor(string $type): string {
return match ($type) {
'primary' => '#007bff',
'success' => '#28a745',
'danger' => '#dc3545',
// 缺少默认抛出 UnhandledMatchError
};
}
// 带复杂表达式的匹配
enum Status {
case Draft;
case Published;
case Archived;
}
function canEdit(Status $status): bool {
return match ($status) {
Status::Draft => true,
Status::Published => false,
Status::Archived => false,
};
}
// 匹配返回不同类型
function process(mixed $value): int|string {
return match (gettype($value)) {
'integer' => $value * 2,
'string' => strtoupper($value),
'array' => count($value),
default => 0,
};
}
// 匹配 vs switch 严格比较
$value = "1";
// Switch 使用 ==(松散)
switch ($value) {
case 1:
echo "Matches with switch
"; // 输出
break;
}
// Match 使用 ===(严格)
$result = match ($value) {
1 => "Matches with match",
"1" => "Strict match", // 此匹配
default => "No match",
};
echo $result; // "Strict match"
匹配表达式由于严格比较和详尽检查要求,比 switch 更安全。
枚举
枚举提供类型安全的一组可能值,用显式类型替换魔术字符串和常量。
<?php
// 基本枚举(PHP 8.1+)
enum Status {
case Pending;
case Approved;
case Rejected;
}
function updateOrder(Status $status): void {
echo "Order status: " . $status->name . "
";
}
updateOrder(Status::Approved);
// 带值的支持枚举
enum Priority: int {
case Low = 1;
case Medium = 2;
case High = 3;
case Critical = 4;
}
$priority = Priority::High;
echo $priority->value; // 3
echo $priority->name; // "High"
// 字符串支持枚举
enum Role: string {
case Admin = 'admin';
case User = 'user';
case Guest = 'guest';
}
// 枚举方法
enum HttpStatus: int {
case OK = 200;
case Created = 201;
case BadRequest = 400;
case Unauthorized = 401;
case NotFound = 404;
case ServerError = 500;
public function isSuccess(): bool {
return $this->value >= 200 && $this->value < 300;
}
public function isError(): bool {
return $this->value >= 400;
}
public function message(): string {
return match ($this) {
self::OK => "Request successful",
self::Created => "Resource created",
self::BadRequest => "Invalid request",
self::Unauthorized => "Authentication required",
self::NotFound => "Resource not found",
self::ServerError => "Internal server error",
};
}
}
$status = HttpStatus::NotFound;
echo $status->isError() ? "Error: " : "Success: ";
echo $status->message();
// 带静态方法的枚举
enum Color: string {
case Red = '#FF0000';
case Green = '#00FF00';
case Blue = '#0000FF';
public static function fromRgb(int $r, int $g, int $b): string {
return sprintf('#%02X%02X%02X', $r, $g, $b);
}
public static function random(): self {
$cases = self::cases();
return $cases[array_rand($cases)];
}
}
// 枚举迭代
foreach (Priority::cases() as $priority) {
echo $priority->name . ": " . $priority->value . "
";
}
// 从值获取枚举
$priority = Priority::from(2); // Priority::Medium
$priority = Priority::tryFrom(10); // null(安全版本)
// 枚举在匹配中
function getPriorityColor(Priority $priority): string {
return match ($priority) {
Priority::Low => 'green',
Priority::Medium => 'yellow',
Priority::High => 'orange',
Priority::Critical => 'red',
};
}
枚举用类型安全的替代品替换魔术字符串和常量,支持IDE自动完成和重构。
属性(注释)
属性将元数据附加到声明,支持声明性配置和基于反射的框架。
<?php
// 内置属性
#[Attribute]
class Route {
public function __construct(
public string $path,
public array $methods = ['GET']
) {}
}
#[Attribute]
class Validate {
public function __construct(
public array $rules
) {}
}
// 使用属性
class UserController {
#[Route('/users', methods: ['GET'])]
public function index(): array {
return ['users' => []];
}
#[Route('/users/{id}', methods: ['GET'])]
#[Validate(rules: ['id' => 'required|integer'])]
public function show(int $id): array {
return ['user' => ['id' => $id]];
}
#[Route('/users', methods: ['POST'])]
#[Validate(rules: ['name' => 'required', 'email' => 'required|email'])]
public function store(array $data): array {
return ['user' => $data];
}
}
// 通过反射读取属性
function getRoutes(string $class): array {
$reflection = new ReflectionClass($class);
$routes = [];
foreach ($reflection->getMethods() as $method) {
$attributes = $method->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$route = $attribute->newInstance();
$routes[] = [
'path' => $route->path,
'methods' => $route->methods,
'handler' => [$class, $method->getName()],
];
}
}
return $routes;
}
$routes = getRoutes(UserController::class);
// 多个属性
#[Attribute(Attribute::TARGET_METHOD)]
class Cache {
public function __construct(
public int $ttl = 3600
) {}
}
#[Attribute(Attribute::TARGET_METHOD)]
class RateLimit {
public function __construct(
public int $maxRequests,
public int $perSeconds
) {}
}
class ApiController {
#[Route('/api/data')]
#[Cache(ttl: 300)]
#[RateLimit(maxRequests: 100, perSeconds: 60)]
public function getData(): array {
return ['data' => []];
}
}
// 类属性
#[Attribute(Attribute::TARGET_CLASS)]
class Entity {
public function __construct(
public string $table
) {}
}
#[Entity(table: 'users')]
class UserEntity {
public int $id;
public string $name;
}
// 属性属性
#[Attribute(Attribute::TARGET_PROPERTY)]
class Column {
public function __construct(
public string $name,
public string $type = 'string'
) {}
}
class Post {
#[Column(name: 'id', type: 'integer')]
public int $id;
#[Column(name: 'title')]
public string $title;
#[Column(name: 'created_at', type: 'datetime')]
public DateTime $createdAt;
}
属性支持依赖注入、路由、验证、ORM映射和其他框架特性的声明性元数据。
命名参数和其他现代特性
命名参数提高了可读性,使可选参数更易使用。
<?php
// 命名参数(PHP 8.0+)
function createUser(
string $name,
int $age,
string $email = '',
bool $active = true
): User {
$user = new User();
$user->name = $name;
$user->age = $age;
$user->email = $email;
$user->active = $active;
return $user;
}
// 传统调用
$user1 = createUser("Alice", 30, "alice@example.com", true);
// 命名参数(任何顺序,跳过默认)
$user2 = createUser(name: "Bob", age: 25);
$user3 = createUser(age: 28, name: "Charlie", active: false);
// 混合位置和命名
$user4 = createUser("Dave", 35, active: false);
// 只读属性(PHP 8.1+)
class Config {
public function __construct(
public readonly string $apiKey,
public readonly string $endpoint
) {}
}
$config = new Config("key123", "https://api.example.com");
// $config->apiKey = "new"; // 错误:只读属性
// 只读类(PHP 8.2+)
readonly class Point {
public function __construct(
public int $x,
public int $y
) {}
}
// 头等可调用语法(PHP 8.1+)
$array = [1, 2, 3, 4, 5];
// 旧方式
$doubled = array_map(fn($n) => $n * 2, $array);
// 头等可调用
function double(int $n): int {
return $n * 2;
}
$doubled = array_map(double(...), $array);
// 新初始化器(PHP 8.1+)
class Service {
public function __construct(
private Logger $logger = new Logger()
) {}
}
// 空安全操作符(PHP 8.0+)
$country = $user?->getAddress()?->getCountry()?->getName();
// 抛出表达式(PHP 8.0+)
$value = $input ?? throw new InvalidArgumentException('Input required');
// str_contains, str_starts_with, str_ends_with(PHP 8.0+)
$text = "Hello, world!";
if (str_contains($text, "world")) {
echo "Contains world
";
}
if (str_starts_with($text, "Hello")) {
echo "Starts with Hello
";
}
if (str_ends_with($text, "world!")) {
echo "Ends with world!
";
}
命名参数使函数调用自文档化,并支持跳过参数列表中间的默认参数。
最佳实践
-
在文件开头使用严格类型声明,以启用严格类型检查并及早捕获类型错误。
-
利用类型属性用于所有类属性,以记录预期类型并启用验证。
-
优先使用匹配而不是switch进行基于值的调度,以受益于严格比较和详尽性。
-
用枚举替换魔术常量,创建类型安全、自文档化的允许值集合。
-
对可选参数使用命名参数,以提高可读性并跳过不必要的默认值。
-
对不可变数据应用只读,以防止意外突变并表达不变性意图。
-
当函数接受多个类型时使用联合类型,而不是接受混合类型。
-
利用属性处理元数据,而不是使用基于反射的框架和工具的文档块。
-
使用构造函数属性提升,以减少具有许多简单属性的类的样板代码。
-
应用空安全操作符,用于对可能为null的对象进行安全属性访问,无需显式null检查。
常见陷阱
-
忘记declare(strict_types=1) 允许类型强制转换,这削弱了类型声明的目的。
-
过度使用联合类型,具有太多替代方案会降低类型安全性优势并使逻辑复杂化。
-
在匹配中未处理所有枚举情况,没有默认值会导致添加新情况时运行时错误。
-
对非穷举集合使用枚举,如用户ID,其中值在编译时未知。
-
错误混合位置和命名参数可能导致错误;位置参数必须位于命名参数之前。
-
在适当情况下未标记只读,允许突变破坏不变性假设。
-
在不理解反射的情况下使用属性,导致无效果的未使用元数据。
-
过度使用混合类型,而不是特定的联合或泛型,会降低类型安全性优势。
-
未检查enum::tryFrom()返回值,在提供无效值时导致崩溃。
-
在适当的情况下使用void而不是never,影响控制流分析。
何时使用此技能
在构建PHP 7.4+或最好是PHP 8.0+的应用程序时使用现代PHP特性,以利用改进的类型安全性和开发体验。
在设计类和函数时应用类型属性和参数,以及早捕获类型错误并改善IDE支持。
在建模固定值集合(如状态、优先级或角色)时使用枚举,而不是使用字符串或整数常量。
基于值(尤其是枚举或类型值)使用匹配表达式进行更清洁的条件逻辑。
使用属性处理框架级元数据,如路由、验证、ORM映射或依赖注入配置。