Skip to content

类型运算符

概述

PHP 的类型运算符 instanceof 用于检查一个变量是否是某个类的实例,或者是否实现了某个接口。在面向对象编程中,instanceof 是类型检查和接口契约验证的核心工具。

PHP 8.0+ 扩展

PHP 8.0 引入了联合类型(Union Types),PHP 8.1 引入了枚举(Enums),使得类型系统更加完善。instanceof 配合这些新特性可以实现更严格的类型安全。

基础概念

instanceof 语法

php
// 基本语法
$object instanceof ClassName
$object instanceof InterfaceName
$object instanceof $variableClass

// 三种检查维度:
// 1. 类检查:是否是某个类的实例
// 2. 接口检查:是否实现了某个接口
// 3. 父类检查:是否是某个父类的子类实例

返回值

instanceof 返回布尔值 truefalse。如果被检查的变量不是对象,始终返回 false(PHP 不会抛出错误)。

语法与代码示例

基本类检查

php
<?php

declare(strict_types=1);

class Animal
{
    public function __construct(
        public string $name
    ) {}
}

class Dog extends Animal
{
    public function bark(): string
    {
        return "Woof!";
    }
}

class Cat extends Animal
{
    public function meow(): string
    {
        return "Meow!";
    }
}

$dog = new Dog('Buddy');
$cat = new Cat('Whiskers');

// 类检查
var_dump($dog instanceof Dog);   // bool(true)
var_dump($dog instanceof Animal); // bool(true) —— 继承关系
var_dump($dog instanceof Cat);    // bool(false)

var_dump($cat instanceof Cat);    // bool(true)
var_dump($cat instanceof Animal); // bool(true)

// 非对象检查
$name = "Hello";
var_dump($name instanceof Animal); // bool(false) —— 不会报错

$nullObj = null;
var_dump($nullObj instanceof Animal); // bool(false)

接口检查

php
<?php

declare(strict_types=1);

interface Serializable
{
    public function serialize(): string;
    public function deserialize(string $data): void;
}

interface Loggable
{
    public function toLogString(): string;
}

class User implements Serializable, Loggable
{
    public function __construct(
        public string $name,
        public string $email
    ) {}

    public function serialize(): string
    {
        return json_encode(['name' => $this->name, 'email' => $this->email]);
    }

    public function deserialize(string $data): void
    {
        $decoded = json_decode($data, true);
        $this->name = $decoded['name'];
        $this->email = $decoded['email'];
    }

    public function toLogString(): string
    {
        return "User({$this->name}, {$this->email})";
    }
}

class Product implements Loggable
{
    public function __construct(public string $title) {}

    public function toLogString(): string
    {
        return "Product({$this->title})";
    }
}

$user = new User('Alice', 'alice@example.com');
$product = new Product('Widget');

// 接口检查
var_dump($user instanceof Serializable); // bool(true)
var_dump($user instanceof Loggable);     // bool(true)
var_dump($product instanceof Serializable); // bool(false)
var_dump($product instanceof Loggable);     // bool(true)

使用变量作为类名

php
<?php

declare(strict_types=1);

class Controller
{
}

class AdminController extends Controller
{
}

class ApiController extends Controller
{
}

// 变量类名
$className = 'AdminController';
$instance = new $className();

var_dump($instance instanceof $className);  // bool(true)
var_dump($instance instanceof Controller);  // bool(true)

// 动态类型检查
function checkType(object $obj, string $typeName): bool
{
    return $obj instanceof $typeName;
}

var_dump(checkType($instance, 'AdminController')); // bool(true)
var_dump(checkType($instance, 'ApiController'));   // bool(false)

详细说明

instanceof 与 is_subclass_of 对比

php
<?php

declare(strict_types=1);

interface Renderable {}

class View implements Renderable {}

class AdminView extends View {}

$adminView = new AdminView();

// instanceof 检查实例
var_dump($adminView instanceof View);      // bool(true)
var_dump($adminView instanceof Renderable); // bool(true)

// is_subclass_of 检查类名(不要求实例化)
var_dump(is_subclass_of(AdminView::class, View::class));       // bool(true)
var_dump(is_subclass_of(AdminView::class, Renderable::class)); // bool(true)

// class_exists 检查类是否存在
var_dump(class_exists(AdminView::class));   // bool(true)
var_dump(class_exists('NonExistent'));     // bool(false)

// instanceof 检查实例,is_subclass_of 检查类
// 在有实例的情况下,两者都可以
// 在只有类名的情况下,用 is_subclass_of

PHP 8.0+ 类型系统的扩展

php
<?php

declare(strict_types=1);

// PHP 8.0+ 联合类型
class Processor
{
    // 参数类型声明中的联合类型
    public function process(int|string $input): int|string
    {
        return is_int($input) ? $input * 2 : strlen($input);
    }
}

// PHP 8.1+ 枚举
interface HasPermission
{
    public function getPermission(): Permission;
}

enum Permission: int
{
    case Read = 1;
    case Write = 2;
    case Admin = 4;
}

class UserRole implements HasPermission
{
    public function __construct(public Permission $permission) {}

    public function getPermission(): Permission
    {
        return $this->permission;
    }
}

$role = new UserRole(Permission::Admin);

// instanceof 也可以用于检查枚举
var_dump($role instanceof HasPermission); // bool(true)

// PHP 8.0+ match 表达式配合类型检查
function handleInput(int|string|float $value): string
{
    return match (true) {
        is_int($value) => "Integer: {$value}",
        is_float($value) => "Float: {$value}",
        is_string($value) => "String: {$value}",
        default => "Unknown type",
    };
}

echo handleInput(42) . "\n";      // Integer: 42
echo handleInput('hello') . "\n"; // String: hello

类型安全的集合处理

php
<?php

declare(strict_types=1);

// 使用 instanceof 进行类型安全的集合过滤
interface Entity
{
    public function getId(): int;
}

class User implements Entity
{
    public function __construct(public int $id, public string $name) {}
    public function getId(): int { return $this->id; }
}

class Product implements Entity
{
    public function __construct(public int $id, public float $price) {}
    public function getId(): int { return $this->id; }
}

function filterByType(array $items, string $className): array
{
    return array_values(array_filter(
        $items,
        fn($item) => $item instanceof $className
    ));
}

$entities = [
    new User(1, 'Alice'),
    new Product(2, 29.99),
    new User(3, 'Bob'),
    new Product(4, 49.99),
];

$users = filterByType($entities, User::class);
$products = filterByType($entities, Product::class);

echo "Users: " . count($users) . "\n";     // 2
echo "Products: " . count($products) . "\n"; // 2

实战示例

多态处理器

php
<?php

declare(strict_types=1);

interface Notifiable
{
    public function send(string $message): void;
}

class EmailNotification implements Notifiable
{
    public function __construct(public string $address) {}

    public function send(string $message): void
    {
        echo "Sending email to {$this->address}: {$message}\n";
    }
}

class SmsNotification implements Notifiable
{
    public function __construct(public string $phoneNumber) {}

    public function send(string $message): void
    {
        echo "Sending SMS to {$this->phoneNumber}: {$message}\n";
    }
}

class NotificationService
{
    /** @var Notifiable[] */
    private array $channels = [];

    public function addChannel(Notifiable $channel): void
    {
        $this->channels[] = $channel;
    }

    public function notifyAll(string $message): void
    {
        foreach ($this->channels as $channel) {
            if ($channel instanceof Notifiable) {
                $channel->send($message);
            }
        }
    }
}

$service = new NotificationService();
$service->addChannel(new EmailNotification('alice@example.com'));
$service->addChannel(new SmsNotification('+1234567890'));
$service->notifyAll('Hello!');

中间件类型检查

php
<?php

declare(strict_types=1);

interface Middleware
{
    public function handle(Request $request, Closure $next): Response;
}

class Request
{
    public function __construct(public string $method, public string $path) {}
}

class Response
{
    public function __construct(public int $status, public string $body) {}
}

class AuthMiddleware implements Middleware
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->path === '/admin' && !isset($request->auth)) {
            return new Response(401, 'Unauthorized');
        }
        return $next($request);
    }
}

function validateMiddleware(object $middleware): void
{
    if (!($middleware instanceof Middleware)) {
        throw new InvalidArgumentException(
            'Middleware must implement Middleware interface'
        );
    }
}

注意事项

  • 非对象变量返回 false$x instanceof SomeClass 不会报错,直接返回 false
  • instanceof 不检查属性:只检查继承和接口关系
  • 避免 instanceof 过多使用:过多的类型检查可能表明设计有问题,考虑使用多态
  • class_exists 先检查:当使用变量类名时,建议先检查类是否存在
  • PHP 8.0+ 优先使用类型声明:参数和返回值类型声明比 instanceof 更安全

最佳实践

  1. 优先使用类型声明:PHP 8.1+ 的联合类型和枚举比运行时 instanceof 检查更安全
  2. instanceof 用于多态分发:在必须区分类型的场景中使用
  3. 检查接口而非具体类$obj instanceof Renderable 优于 $obj instanceof PdfRenderer
  4. 使用 ::class 常量$obj instanceof User::class 优于字符串
  5. try/catch 替代 if + instanceof:对于类型相关的错误,考虑异常处理

参考链接