匿名类
概述
匿名类(Anonymous Class)是 PHP 7.0 引入的特性,允许创建没有命名的、一次性使用的类实例。匿名类在创建时同时定义和实例化,非常适合回调、测试 mock、简单数据传输对象等场景。它们可以继承其他类、实现接口和使用 Trait,但不支持序列化。
版本要求
匿名类是 PHP 7.0+ 特性。
基础概念
匿名类的特点
- 没有类名,通过
new class语法创建 - 可以实现接口、继承类、使用 Trait
- 自动分配一个内部类名(基于所在文件和行号)
- 不支持序列化(
serialize()/unserialize()) - 生命周期仅限于创建时的作用域
语法与代码
基本语法
php
<?php
declare(strict_types=1);
// 创建匿名类实例
$logger = new class {
public function log(string $message): void
{
echo "[LOG] {$message}" . PHP_EOL;
}
};
$logger->log('Application started');
// [LOG] Application started实现接口
php
<?php
declare(strict_types=1);
interface Processor
{
public function process(string $input): string;
}
$upperCase = new class implements Processor {
public function process(string $input): string
{
return strtoupper($input);
}
};
$reverser = new class implements Processor {
public function process(string $input): string
{
return strrev($input);
}
};
echo $upperCase->process('hello'); // HELLO
echo $reverser->process('hello'); // olleh继承类
php
<?php
declare(strict_types=1);
abstract class BaseTransformer
{
abstract public function transform(array $data): array;
public function apply(array $data, callable $callback): array
{
$transformed = $this->transform($data);
return $callback($transformed);
}
}
$transformer = new class extends BaseTransformer {
public function transform(array $data): array
{
return array_map(fn($item) => trim($item), $data);
}
};
$result = $transformer->apply([' hello ', ' world '], fn($d) => $d);
// ['hello', 'world']使用 Trait
php
<?php
declare(strict_types=1);
trait Loggable
{
private array $logs = [];
public function log(string $message): void
{
$this->logs[] = $message;
}
public function getLogs(): array
{
return $this->logs;
}
}
$service = new class {
use Loggable;
public function doWork(): void
{
$this->log('Work started');
// ... work ...
$this->log('Work finished');
}
};
$service->doWork();
print_r($service->getLogs());
// ['Work started', 'Work finished']构造函数参数
php
<?php
declare(strict_types=1);
$validator = new class('email', 'name') {
private array $requiredFields;
public function __construct(string ...$requiredFields)
{
$this->requiredFields = $requiredFields;
}
public function validate(array $data): array
{
$errors = [];
foreach ($this->requiredFields as $field) {
if (empty($data[$field])) {
$errors[] = "Field '{$field}' is required";
}
}
return $errors;
}
};
$errors = $validator->validate(['name' => 'Alice']);
print_r($errors);
// ['Field \'email\' is required']闭包绑定与匿名类
php
<?php
declare(strict_types=1);
class EventDispatcher
{
/** @var array<string, callable> */
private array $listeners = [];
public function on(string $event, callable $listener): void
{
$this->listeners[$event][] = $listener;
}
public function emit(string $event, mixed $payload): void
{
foreach ($this->listeners[$event] ?? [] as $listener) {
$listener($payload);
}
}
}
$dispatcher = new EventDispatcher();
$dispatcher->on('user.created', new class {
public function __invoke(array $user): void
{
echo "New user: {$user['name']}" . PHP_EOL;
}
});
$dispatcher->emit('user.created', ['name' => 'Bob']);
// New user: Bob详细说明
匿名类的自动命名
PHP 会为每个匿名类分配一个基于文件路径和行号的内部名称:
php
<?php
declare(strict_types=1);
$obj = new class {};
echo get_class($obj);
// class@anonymous /path/to/file.php:3$0匿名类与命名类的对比
| 特性 | 命名类 | 匿名类 |
|---|---|---|
| 可复用 | 是 | 否(一次性) |
| 可序列化 | 是 | 否 |
| 实现接口 | 是 | 是 |
| 继承类 | 是 | 是 |
| 使用 Trait | 是 | 是 |
| 类型声明 | 可作为参数/返回类型 | 不能 |
| 可测试性 | 高(可 mock) | 低(内联定义) |
闭包中的变量捕获
php
<?php
declare(strict_types=1);
$prefix = '[APP]';
$logger = new class($prefix) {
private string $prefix;
public function __construct(string $prefix)
{
$this->prefix = $prefix;
}
public function log(string $message): void
{
echo "{$this->prefix} {$message}" . PHP_EOL;
}
};
$logger->log('Ready');
// [APP] Ready实战示例
场景一:测试中的 Mock 对象
php
<?php
declare(strict_types=1);
interface UserRepositoryInterface
{
public function findById(int $id): ?array;
public function save(array $user): int;
}
class UserService
{
public function __construct(
private readonly UserRepositoryInterface $repository
) {}
public function getUserName(int $id): string
{
$user = $this->repository->findById($id);
return $user['name'] ?? 'Unknown';
}
}
// 测试中使用匿名类作为 mock
$mockRepo = new class implements UserRepositoryInterface {
public function findById(int $id): ?array
{
return ['id' => $id, 'name' => 'Test User'];
}
public function save(array $user): int
{
return 1;
}
};
$service = new UserService($mockRepo);
echo $service->getUserName(1); // Test User场景二:回调接口实现
php
<?php
declare(strict_types=1);
interface Comparator
{
public function compare(mixed $a, mixed $b): int;
}
function sortWith(array $items, Comparator $comparator): array
{
usort($items, fn($a, $b) => $comparator->compare($a, $b));
return $items;
}
$numbers = [3, 1, 4, 1, 5, 9];
// 使用匿名类实现排序比较
$sorted = sortWith($numbers, new class implements Comparator {
public function compare(mixed $a, mixed $b): int
{
return $a <=> $b;
}
});
print_r($sorted);
// [1, 1, 3, 4, 5, 9]场景三:中间件匿名实现
php
<?php
declare(strict_types=1);
interface Middleware
{
public function handle(array $request, callable $next): array;
}
class Pipeline
{
/** @var Middleware[] */
private array $middlewares = [];
public function pipe(Middleware $middleware): self
{
$this->middlewares[] = $middleware;
return $this;
}
public function run(array $request, callable $destination): array
{
$pipeline = array_reduce(
array_reverse($this->middlewares),
function (callable $next, Middleware $middleware): callable {
return fn(array $request) => $middleware->handle($request, $next);
},
$destination
);
return $pipeline($request);
}
}
$pipeline = new Pipeline();
$pipeline->pipe(new class implements Middleware {
public function handle(array $request, callable $next): array
{
$request['auth_checked'] = true;
return $next($request);
}
});
$result = $pipeline->run(['data' => 'test'], fn($r) => $r);
print_r($result);注意事项
注意事项
- 匿名类不能被序列化(
serialize()会报错) - 匿名类不能作为类型声明(参数类型、返回值类型)
- 匿名类有构造函数参数限制——必须在创建时传入
- 不要在性能敏感的循环中创建匿名类(每次创建新实例)
小贴士
- 在测试中优先使用匿名类创建 mock,避免创建完整的 mock 类
- 匿名类适合一次性使用的回调、适配器和装饰器
- 对于可复用的行为,使用命名类而非匿名类
最佳实践
1. 保持匿名类简短
php
<?php
declare(strict_types=1);
// 推荐 - 简短明确
$mock = new class implements LoggerInterface {
public function log(string $msg): void { /* empty */ }
};
// 不推荐 - 复杂逻辑
$mock = new class implements RepositoryInterface {
// 20+ 行逻辑...
};2. 使用构造函数注入依赖
php
<?php
declare(strict_types=1);
$cache = new class(new \ArrayObject()) {
public function __construct(private readonly \ArrayObject $store) {}
public function get(string $key): mixed
{
return $this->store[$key] ?? null;
}
public function set(string $key, mixed $value): void
{
$this->store[$key] = $value;
}
};