PHP `new static()`与后期静态绑定

好嘞!各位编程界的弄潮儿们,大家好!我是你们的编程老司机,今天咱们来聊聊PHP里一个有点意思,又有点小脾气的家伙——new static()以及它背后的“后期静态绑定”(Late Static Binding,简称LSB)。

准备好了吗?系好安全带,咱们发车啦!🚀

PHP中的new static():身世之谜与妙用

1. 初识new static():并非你想象的那么简单

各位可能对new self()比较熟悉,它就像一个乖宝宝,总是老老实实地创建当前类的实例。但new static()就不一样了,它就像一个调皮的孩子,会根据调用时的上下文,动态地创建实例。

用人话说,new self()永远指向定义它的那个类,而new static()则指向最终调用它的那个类

为了更好地理解,我们先来看一个例子:

<?php

class A {
    public static function create() {
        return new self(); // 注意这里是 new self()
    }

    public static function createStatic() {
        return new static(); // 注意这里是 new static()
    }

    public function whoAmI() {
        echo "I am " . get_class($this) . "n";
    }
}

class B extends A {
    public function whoAmI() {
        echo "I am B, not A!n";
    }
}

$a = A::create();
$a->whoAmI(); // 输出: I am A

$b = B::create();
$b->whoAmI(); // 输出: I am A (注意这里仍然是A!)

$c = A::createStatic();
$c->whoAmI(); // 输出: I am A

$d = B::createStatic();
$d->whoAmI(); // 输出: I am B, not A! (看到了吗?惊喜!)

?>

是不是有点晕?没关系,我们慢慢来分析。

  • A::create()$a: A::create()new self()创建了一个A类的实例,这很正常,没有任何悬念。
  • B::create()$b: 关键来了!B::create()继承了A类的create()方法,但由于create()方法里用的是new self(),所以创建的仍然是A类的实例,而不是B类的实例!这就是new self()的局限性。
  • A::createStatic()$c: A::createStatic()new static()创建了一个A类的实例,和new self()一样,没有惊喜。
  • B::createStatic()$d: 高潮来了!B::createStatic()继承了A类的createStatic()方法,但由于createStatic()方法里用的是new static(),所以创建的是B的实例!这就是new static()的强大之处。

用一张表格来总结一下:

调用方式 使用方法 创建的实例
A::create() new self() A
B::create() new self() A
A::createStatic() new static() A
B::createStatic() new static() B

看到了吗?new static()就像一个变色龙,会根据调用它的上下文,动态地选择要创建的类。

2. 后期静态绑定 (Late Static Binding): new static() 的幕后英雄

new static()之所以如此神奇,背后的功臣就是后期静态绑定(LSB)。 LSB 是一种机制,它允许你在运行时确定静态调用的类。 简单来说,就是让static关键字不再死板地指向定义它的那个类,而是指向最终调用它的那个类。

PHP 通过static::关键字来实现 LSB。 new static()实际上就是new static::class的语法糖,它会动态地获取当前类的名字,然后创建该类的实例。

3. new static() 的应用场景:工厂模式、单例模式、可链式操作

new static()在一些设计模式中非常有用,可以提高代码的灵活性和可扩展性。

  • 工厂模式 (Factory Pattern): new static()可以轻松地创建不同类型的对象,而无需修改工厂类本身。

    <?php
    
    abstract class Product {
        abstract public function operation();
    }
    
    class ConcreteProductA extends Product {
        public function operation() {
            return "Product An";
        }
    }
    
    class ConcreteProductB extends Product {
        public function operation() {
            return "Product Bn";
        }
    }
    
    class Factory {
        public static function createProduct($type) {
            switch ($type) {
                case 'A':
                    return new ConcreteProductA(); // 可以用 new static() 替换,更灵活
                case 'B':
                    return new ConcreteProductB(); // 可以用 new static() 替换,更灵活
                default:
                    throw new Exception("Invalid product type");
            }
        }
    
        public static function create() {
            return new static(); // 关键在这里
        }
    
        public function operation() {
            return "Base Factory";
        }
    }
    
    class ConcreteFactoryA extends Factory {
        public function operation() {
            return "Concrete Factory An";
        }
    
        public static function create() {
            return new static(); // 创建 ConcreteFactoryA 的实例
        }
    }
    
    $factoryA = ConcreteFactoryA::create();
    echo $factoryA->operation(); // 输出: Concrete Factory A
    
    $productA = $factoryA->createProduct('A'); //这里为了演示 Factory模式,所以没有使用 new static()
    echo $productA->operation(); //输出: Product A
    
    ?>
  • 单例模式 (Singleton Pattern): 虽然单例模式通常用new self()实现,但new static()可以实现更灵活的单例模式,允许子类拥有自己的单例实例。

    <?php
    
    class Singleton {
        private static $instance = null;
    
        protected function __construct() {
            // 防止外部实例化
        }
    
        public static function getInstance() {
            if (static::$instance === null) {
                static::$instance = new static(); // 使用 new static()
            }
            return static::$instance;
        }
    
        // 防止克隆
        private function __clone() {}
    
        // 防止反序列化
        private function __wakeup() {}
    
        public function doSomething() {
            echo "Singleton instance doing somethingn";
        }
    }
    
    class MySingleton extends Singleton {
        public function doSomething() {
            echo "MySingleton instance doing somethingn";
        }
    }
    
    $singleton1 = Singleton::getInstance();
    $singleton1->doSomething(); // 输出: Singleton instance doing something
    
    $mySingleton1 = MySingleton::getInstance();
    $mySingleton1->doSomething(); // 输出: MySingleton instance doing something
    
    ?>
  • 可链式操作 (Fluent Interface): new static() 可以用于创建可链式操作的对象,方便进行方法调用。

    <?php
    
    class QueryBuilder {
        protected $table;
        protected $where = [];
    
        public static function table($table) {
            $instance = new static();
            $instance->table = $table;
            return $instance;
        }
    
        public function where($column, $operator, $value) {
            $this->where[] = [$column, $operator, $value];
            return $this;
        }
    
        public function getQuery() {
            $query = "SELECT * FROM {$this->table}";
            if (!empty($this->where)) {
                $whereClauses = [];
                foreach ($this->where as $condition) {
                    $whereClauses[] = "{$condition[0]} {$condition[1]} '{$condition[2]}'";
                }
                $query .= " WHERE " . implode(' AND ', $whereClauses);
            }
            return $query;
        }
    }
    
    class MyQueryBuilder extends QueryBuilder {
    
    }
    
    $query = MyQueryBuilder::table('users')
        ->where('age', '>', 18)
        ->where('city', '=', 'Beijing')
        ->getQuery();
    
    echo $query . "n"; // 输出: SELECT * FROM users WHERE age > '18' AND city = 'Beijing'
    
    ?>

4. new static() 的注意事项:并非万能药

虽然new static()很强大,但也不是万能的。在使用时,需要注意以下几点:

  • 理解 LSB 的本质: 只有真正理解了 LSB 的工作原理,才能正确地使用new static()。 否则,可能会出现意想不到的结果。
  • 避免过度使用: new static()虽然灵活,但也会增加代码的复杂性。 在不需要动态创建实例的情况下,还是应该优先使用new self()
  • 考虑性能问题: LSB 的实现会带来一定的性能开销。 在高并发的场景下,需要仔细评估new static()的使用是否会影响性能。

5. 深入理解static::class

new static()本质上是 new static::class的语法糖。 static::class 是 PHP 5.5 引入的一个常量,它允许你在运行时获取当前类的名字。

<?php

class A {
    public static function getClassName() {
        return static::class;
    }
}

class B extends A {
}

echo A::getClassName() . "n"; // 输出: A
echo B::getClassName() . "n"; // 输出: B

?>

static::class 在命名空间和自动加载方面非常有用。它可以帮助你动态地加载类,而无需硬编码类名。

总结:new static(),让你的代码更灵活!

总而言之,new static()是PHP中一个非常有用,但又有点复杂的特性。 掌握了new static(),你就可以写出更加灵活、可扩展的代码,让你的程序更加健壮。

用一句话来概括:new static()就像一个魔法棒,可以根据你的需求,动态地创建不同类型的对象。

希望今天的讲解对大家有所帮助。 如果你觉得这篇文章对你有帮助,请点个赞,分享给你的朋友们。 咱们下期再见! 😉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注