【问题标题】:polymorphic static and non-static functions in PHPPHP中的多态静态和非静态函数
【发布时间】:2026-01-05 13:10:01
【问题描述】:

我想在 PHP 中创建一个简单的Path 类。我决定使用一些类似多态的机制来实现它,但我发现没有办法同时实现它:静态和非静态函数。您对如何优雅地实现absolute() 函数有任何想法吗?

class Path {

    private $path = '';

    // Some code here [...]

    public function absoulte() {
        return "http://$_SERVER[HTTP_HOST]$this->path";
    }

    public static function absoulte($path) {
        return "http://$_SERVER[HTTP_HOST]$path";
    }
}

【问题讨论】:

  • 好吧,要不两次实现相同的函数,您可以创建一个实例并在其上调用非静态函数:public static function absolute($path) { return (new static($path))->absolute(); }(期望有一个接受路径作为唯一变量的构造函数)
  • @Namoshek,听起来很有趣,但我不明白。如何创建具有相同名称的两个函数?我是 PHP 新手,你能解释一下吗?

标签: php polymorphism static-methods


【解决方案1】:

好的,我会试一试,你看看它是否对你有帮助。 :)

我认为实际上有很多方法可以实现您所描述的公共接口,尽管它们的编码和维护看起来都相当复杂。因此,如果不是真的必要,我的建议是不要走这条路。我会举一些例子......


可能的解决方案1:使用__call__callStatic

我们可以简单地使用__call__callStatic 作为我们方法的代理,并使用稍微不同的参数调用实际方法。顶部的 PhpDoc 应该支持 IDE 了解这里发生的事情。

/**
 * @method string absolute()
 * @method static string absolute(string $path)
 */
class Path
{
    protected $path;

    public function __construct(string $path)
    {
        $this->path = $path;
    }

    public function __call(string $name, array $args)
    {
        if ($name === 'absolute') {
            return self::getAbsolutePath($this->path);
        }

        // ... other functions ...
    }

    public function __callStatic(string $name, array $args)
    {
        if ($name === 'absolute' && count($args) >= 1) {
            return self::getAbsolutePath($args[0]);
        }

        // ... other functions ...
    }

    private static function getAbsolutePath(string $path): string
    {
        return "http://{$_SERVER[HTTP_HOST]}{$path}";
    }
}

可能的解决方案 2:对执行环境做出反应

因为我们可以以静态方式和非静态方式访问 PHP 中的方法,所以我们只需使用我们手中的信息来返回正确的结果。

/**
 * @method string absolute()
 * @method static string absolute(string $path)
 */
class Path
{
    protected $path;

    public function __construct(string $path)
    {
        $this->path = $path;
    }

    public function absolute(?string $path): string
    {
        if (isset($this) && $this instanceof self) {
            $path = $this->path;
        }

        return "http://{$_SERVER[HTTP_HOST]}{$path}";
    }
}

其他解决方案

我认为还有其他方法可以实现这一点,可能是使用代理类左右。但正确的做法也取决于我们迄今为止所看到的其他要求。因为一个参数$path 很容易处理,但是你的类越复杂,实现不同场景的工作就越多。

最后,我想继续我上面的评论:如果有解决办法,尽量不要执行两次。稍后您会发现自己在多个地方寻找错误。因此,最好只有两个不同的接口和一个通用的实现。它实际上甚至是有意义的,因为相同的方法名称不一定在静态和非静态环境中都有意义。所以考虑使用这样的东西:

/**
 * @method string absolute()
 * @method static string absolute(string $path)
 */
class Path
{
    protected $path;

    public function __construct(string $path)
    {
        $this->path = $path;
    }

    public function getAbsolutePath(): string
    {
        return self::absolute($this->path);
    }

    public static function absolute(string $path): string
    {
        return "http://{$_SERVER[HTTP_HOST]}{$path}";
    }
}

【讨论】:

  • 对我帮助很大!谢谢!你向我解释了我从不知道的关于 PHP 的所有事情 :)
【解决方案2】:

您可以尝试使用“__callStatic”。如果您在此类上调用任何静态函数,它将触发。 “$name” 将是方法名称,“$arguments” 是传递的任何参数。 :D

class Path {

    private $path = '';

    // Some code here [...]

    public function absoulte() {
        return "http://$_SERVER[HTTP_HOST]$this->path";
    }

   public static function __callStatic($name,$arguments) {
       if ($name == "absoulte") {  return "http://$_SERVER[HTTP_HOST]$arguments"; }
   }
}

【讨论】:

    【解决方案3】:

    它已经解决了,但也许会帮助别人。这是使用func_num_args()func_get_arg() 函数的另一种方法,也可以。

    class Path {
    
        private $path = '';
    
        // ...
    
        public function absolute() {
            switch (func_num_args()) {
                case 0: return "http://" . $_SERVER[HTTP_HOST] . $this->path;
                case 1: return "http://" . $_SERVER[HTTP_HOST] . func_get_arg(0);
                default: trigger_error("Invalid number of arguments", E_USER_ERROR);
            }
        }
    }
    

    【讨论】: