Skip to content

后期静态绑定

概述

后期静态绑定(Late Static Binding, LSB)是 PHP 5.3 引入的特性,通过 static:: 关键字在运行时解析被调用的类,而非在编译时绑定到定义时的类。self:: 始终指向定义方法的类,而 static:: 指向实际调用方法的类。这在工厂模式、继承链中的方法调用等场景中非常重要。

基础概念

self:: vs static::

关键字绑定时机指向
self::编译时定义方法的类
static::运行时实际调用方法的类
parent::编译时父类

get_called_class()

get_called_class() 返回实际调用方法的类名,与 static:: 的行为一致。

语法与代码

self:: 的问题

php
<?php

declare(strict_types=1);

class Base
{
    public static function create(): static
    {
        // self:: 始终指向 Base,而非子类
        return new self();
    }

    public static function who(): string
    {
        return self::class;
    }
}

class Child extends Base
{
}

echo Base::who();    // "Base"
echo Child::who();   // "Base" — 不是期望的 "Child"

static:: 解决方案

php
<?php

declare(strict_types=1);

class Base
{
    public static function create(): static
    {
        // static:: 在运行时解析为实际调用的类
        return new static();
    }

    public static function who(): string
    {
        return static::class;
    }
}

class Child extends Base
{
}

echo Base::who();    // "Base"
echo Child::who();   // "Child" — 符合预期

get_called_class()

php
<?php

declare(strict_types=1);

class Animal
{
    public static function identify(): string
    {
        return get_called_class();
    }
}

class Dog extends Animal {}
class Cat extends Animal {}

echo Animal::identify();  // "Animal"
echo Dog::identify();    // "Dog"
echo Cat::identify();    // "Cat"

工厂模式中的使用

php
<?php

declare(strict_types=1);

abstract class Model
{
    protected static string $table = '';

    public static function find(int $id): ?static
    {
        // static:: 保证了在子类调用时使用子类的表名
        $table = static::$table;
        echo "SELECT * FROM {$table} WHERE id = {$id}";
        // 实际查询逻辑...
        return null;
    }

    public static function all(): array
    {
        $table = static::$table;
        echo "SELECT * FROM {$table}";
        return [];
    }

    public function save(): void
    {
        $table = static::$table;
        echo "INSERT INTO {$table} ...";
    }
}

class User extends Model
{
    protected static string $table = 'users';
}

class Order extends Model
{
    protected static string $table = 'orders';
}

User::find(1);    // SELECT * FROM users WHERE id = 1
Order::find(1);    // SELECT * FROM orders WHERE id = 1
User::all();       // SELECT * FROM users

继承链中的方法转发

php
<?php

declare(strict_types=1);

class BaseService
{
    public static function process(): string
    {
        return static::handle();
    }

    protected static function handle(): string
    {
        return 'BaseService::handle';
    }
}

class UserService extends BaseService
{
    protected static function handle(): string
    {
        return 'UserService::handle';
    }
}

class AdminService extends UserService
{
    protected static function handle(): string
    {
        return 'AdminService::handle';
    }
}

echo BaseService::process();  // BaseService::handle
echo UserService::process();  // UserService::handle
echo AdminService::process(); // AdminService::handle

详细说明

static:: 的解析规则

  1. static::运行时根据实际调用上下文解析
  2. 即使在父类方法中使用 static::,也会解析为子类
  3. static:: 同样适用于属性访问(static::$property
  4. static:: 可以与非静态方法一起使用(不推荐)

static:: 在非静态方法中

php
<?php

declare(strict_types=1);

class ParentClass
{
    protected string $prefix = 'parent';

    public function getPrefix(): string
    {
        return static::$prefix;  // 运行时解析
    }
}

class ChildClass extends ParentClass
{
    protected string $prefix = 'child';
}

$parent = new ParentClass();
$child = new ChildClass();

echo $parent->getPrefix();  // "parent"
echo $child->getPrefix();   // "child"

注意

在非静态方法中使用 static:: 访问静态属性时,需要确保子类也声明了该属性。

forward_static_call()

php
<?php

declare(strict_types=1);

class Processor
{
    public static function process(string $type): string
    {
        return forward_static_call([static::class, 'handle' . ucfirst($type)]);
    }
}

class TextProcessor extends Processor
{
    public static function handleText(): string
    {
        return 'Text processed';
    }
}

echo TextProcessor::process('text');  // "Text processed"

实战示例

场景一:Active Record 模式

php
<?php

declare(strict_types=1);

abstract class ActiveRecord
{
    protected static string $tableName = '';
    protected array $attributes = [];

    public static function find(int $id): ?static
    {
        $sql = sprintf(
            'SELECT * FROM %s WHERE id = %d',
            static::$tableName,
            $id
        );
        // 模拟查询
        $data = ['id' => $id, 'name' => 'Test'];
        $instance = new static();
        $instance->attributes = $data;
        return $instance;
    }

    public static function create(array $attributes): static
    {
        $instance = new static();
        $instance->attributes = $attributes;
        // INSERT INTO static::$tableName ...
        return $instance;
    }

    public function __get(string $name): mixed
    {
        return $this->attributes[$name] ?? null;
    }
}

class User extends ActiveRecord
{
    protected static string $tableName = 'users';
}

class Product extends ActiveRecord
{
    protected static string $tableName = 'products';
}

$user = User::find(1);
$product = Product::find(10);
$newUser = User::create(['name' => 'Alice']);

场景二:Builder 模式

php
<?php

declare(strict_types=1);

abstract class QueryBuilder
{
    protected array $wheres = [];
    protected int $limit = 0;
    protected int $offset = 0;

    public static function query(): static
    {
        return new static();
    }

    public function where(string $column, mixed $value): static
    {
        $this->wheres[$column] = $value;
        return $this;
    }

    public function limit(int $limit): static
    {
        $this->limit = $limit;
        return $this;
    }

    abstract public function get(): array;
}

class UserQuery extends QueryBuilder
{
    public function active(): static
    {
        return $this->where('status', 'active');
    }

    public function get(): array
    {
        // 实现查询逻辑
        return [
            'table' => 'users',
            'wheres' => $this->wheres,
            'limit' => $this->limit,
        ];
    }
}

$users = UserQuery::query()->active()->limit(10)->get();

注意事项

注意事项

  • self:: 在编译时绑定,始终指向定义方法的类
  • static:: 在运行时绑定,指向实际调用的类
  • 在静态方法中 new self() 会创建定义类的实例,new static() 创建调用类的实例
  • static:: 不能用于常量访问(PHP 8.3 前不支持 static::CONST

小贴士

  • 在工厂方法中使用 new static() 而非 new self()
  • 在继承链中需要多态行为时使用 static::
  • 对于明确的调用,使用 self::parent::

最佳实践

1. 工厂方法使用 static 返回类型

php
<?php

declare(strict_types=1);

abstract class Entity
{
    public static function create(array $data): static
    {
        return new static();
    }
}

2. 明确区分 self 和 static

php
<?php

declare(strict_types=1);

class Configuration
{
    private static array $defaults = [];

    // 明确需要当前类的 defaults
    public static function getDefaults(): array
    {
        return self::$defaults;
    }

    // 需要子类的实现
    public static function getTable(): string
    {
        return static::$table;
    }
}

参考链接