Skip to content

匿名类

概述

匿名类(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;
    }
};

参考链接