有没有一个原生接口可以匹配“string”和“实现__toString()的对象”?

问题描述 投票:0回答:4

我们有实现

__toString()
的对象:

class Foo {
    public function __toString()
    {
        return 'bar';
    }
}

然后我们有返回

string
的函数或实现
__toString()
的对象,如上面的示例。

当使用返回类型

string
表示字符串时当然可以:

function getString():string {
    return 'Works!';
}

返回

Foo
类型的对象不起作用,因为它不是
string
:

function getString(Foo $foo):string {
    return $foo; // Fatal TypeError!
}

是否有任何 PHP 接口可以用来输入提示

string
以及实现该未知接口的对象,例如
Printable
还是
Stringable
(笑)?

目标是能够返回

string
或实现特定接口的对象(这将强制实现
__toString
)。

php interface type-hinting
4个回答
9
投票

从 PHP 8.0 开始,有一个 magic

Stringable
接口,任何具有
__toString
方法的类都会自动实现它(尽管 PHP 在该语言的其他任何地方都没有自动/结构接口的概念)。

请注意,它匹配此类对象,因此为了也匹配实际字符串,您需要使用union类型(也在PHP 8.0中添加),并将您的类型声明为

string|Stringable

这个答案的其余部分最初是在添加

Stringable
之前写的,并且是我坚持的观点,因为它不是一个好的设计。


您需要问的设计问题是,您实际上在这里代表什么合约?从调用代码的角度来看,不能保证返回值实际上可以直接用作字符串 - 有许多操作对于字符串和对象会有不同的行为,或者接受字符串但不接受自动投射对象。因此,这样的提示实际上保证的唯一事情是

(string)$foo
将给出一个字符串。

所以你有几个选择:

  • 而不是通用的
    Stringable
    ,提出一个更有意义的接口来返回;例如,也许您实际返回的是
    Loggable
    的东西。这样,包装字符串可以给您带来更具体的好处:您可以向接口添加其他方法,并且调用代码在调用这些方法之前不需要进行“对象或字符串”检查。
  • 如果调用代码仅将返回值用作字符串,则根本不要返回该对象。将函数标记为返回
    string
    ,并在返回任何对象之前将其转换为字符串。
  • 如果希望调用代码检查返回值并对不同类型的对象执行不同的操作,则根本不要限制返回类型。您可能想要添加联合类型,但前提是有一个简短的可能性列表;无论如何,过于宽泛的返回类型作为合约几乎没有用处。

5
投票

PHP 8 开始,您将拥有 Union Types 和新接口 Stringable。您可以结合使用这两个概念来获得您想要的结果。

function getString(Stringable|string $foo) : string {
    // Fatal TypeError if $foo is not a string or a class that implements Stringable
    return $foo instanceof Stringable ? $foo->__toString() : $foo;
}

在 PHP 8 中,任何具有

__toString()
方法的类都会在底层自动实现 Stringable 接口。


3
投票

您可以将字符串装箱在一个包装类中,该类只接受

c-tor
中的字符串,并有一个
toString()
方法返回它。

这个包装类将实现您的 Stringable 接口,就像您的其他类一样。

然后你可以提供一个返回类型提示方法,它返回 Stringable 类型的对象。

此方法可以决定它是返回一个对象还是只是字符串的包装器,因为它们都实现了 Stringable。

但是,这是为了与仅返回混合并检查调用方法中返回的值类型进行平衡。

此外,如果您的对象实现了魔法方法

__toString
,那么所有调用方法都可以始终在返回值上应用
cast (string)

如果返回值是一个字符串,那么它就一直是一个字符串。

如果它是实现魔术方法的对象之一,那么您将获得该字符串。

然而在这种情况下你不会有返回类型提示。


2
投票

实现

__toString
的字符串和对象不可互换,例如
$s[1]
将在对象上失败(除非它实现了其他一些魔术方法)。在参数被强制为字符串的上下文中,对象“可能”是有效的替代品,但这并不适用于您可以对字符串执行的所有可能的操作。所以,不,没有原生接口,因此您也不应该混合这些类型。

© www.soinside.com 2019 - 2024. All rights reserved.