使抽象 class 方法只能从子 classes 公开访问

Make abstract class method only publicly accessible from child classes

首先,我们谈论的是 PHP 7.4.10,但任何通用英特尔都值得赞赏!

问题总结:我想在抽象 class 中定义一个静态方法,这样只能从子 classes 扩展抽象 class 中公开调用该方法 class 但不是来自摘要 class 本身。抱歉,如果我在这里太基础了,但我实际上已经搜索了几个小时来寻找答案,甚至找不到关于该主题的任何讨论。

让我们考虑以下示例(注释中的解释)。我希望能够调用 Apple::printName()Pear::printName() 但不能调用 Fruit::printName().

abstract class Fruit
{
    /*
     * Oblige every child class to define a string name, nothing unusual
     */
    protected abstract static function name() : string;

    /*
     * The problem is with the access modifier of this method here
     *** 
     * If it is public, everything is fine with Apple::printName() and Pear::printName(), 
     * but one can call Fruit::printName() from outside,
     * resulting in PHP Error: Cannot call abstract method Fruit::name()
     * this is still sort of okay, since an error will be thrown anyway, 
     * but I don't want the runtime to even enter the method's body
     * I'd like to get an access restriction error.
     *** 
     * If it is protected, then we automatically can't call Apple::printName nor Pear::printName()
     ***
     * So, is there a way to define the parent static method only publicly accessible from child classes without copying code?
     */
    public static function printName()
    {
        return "My name is: " . static::name();
    }
}

class Apple extends Fruit
{
    protected static function name() : string
    {
        return "apple";
    }
}

class Pear extends Fruit
{
    protected static function name() : string
    {
        return "pear";
    }
}

echo Apple::printName(); //prints "My name is: apple"
echo Pear::printName(); //prints "My name is: pear"
echo Fruit::printName(); //PHP Error: Cannot call abstract method Fruit::name() at line...

对于如何实现所需行为的任​​何替代方法,我也持开放态度。

public static function printName() {}

这是具体方法的定义。人们可能会认为调用此方法是可以的,因为它是非抽象 public 方法。那么,为什么 Fruit 没有对它做任何事情呢?

所以一个解决方案是定义一个 traituse 它只在那些实际上 需要 的里面?

trait Printer { 
    
    public static function printName()
    {
        return "My name is: " . static::name();
    }   
}

abstract class Fruit
{
    protected abstract static function name() : string;
}

class Apple extends Fruit
{
    use Printer;
    protected static function name() : string
    {
        return "apple";
    }
}

class Pear extends Fruit
{
    use Printer;
    protected static function name() : string
    {
        return "pear";
    }
}

但是,如果您的 Fruit 只是 child 类 的定义,就像您在评论中提到的那样,您恰好有 PHP 8 :) 那么也许就不要让 printName 静态

public function printName()
{
    return "My name is: " . static::name();
}

$a = new Apple();
$p = new Pear();

echo $a->printName();
echo $p->printName();

现在 echo Fruit::printName(); 将为您提供您一直在寻找的致命错误。

Fatal error: Uncaught Error: Non-static method Fruit::printName() cannot be called statically

您可以检查您的实例是否是子类,如果不是则退出

abstract class A1 {
    public static function childrenOnly() {
        return is_subclass_of(new static, 'A1');
    }
}

class A2 extends A1 {
}
Al::childrenOnly(); // errors out
A2::childrenOnly(); // true

所以在阅读了所有的答案后,我想出了一个 workaround-ish 解决方案,最终决定赞成 @chilliNUT 的建议。我最终按如下方式进行:

abstract class Fruit
{
    protected abstract static function name() : string;

    /*
     * Basically, if you call the default method from any child (not necessarily a direct child) class, no problems,
     * but if you call it directly from the baseclass, you get the somewhat acceptable exception :)
     */
    public static function printName()
    {
        if(!is_subclass_of(static::class, self::class))
        {
            throw new \BadMethodCallException("Can't call base Fruit class methods directly!");
        }

        return "My name is: " . static::name();
    }
}

//those are abstract now, just to match the intention of them being utility singletons
abstract class Apple extends Fruit
{
    protected static function name() : string
    {
        return "apple";
    }
}

abstract class Pear extends Fruit
{
    protected static function name() : string
    {
        return "pear";
    }
}

无论如何,从@Rain的回答我可以看出为什么这往往是一种不好的做法,实际上不是继续发展的最佳方向,所以我会考虑我要实现的目标的不同策略。终于满足了我的好奇心!感谢大家的参与!