PHP 8.0 新特性: Stringable 接口
PHP 8.0 添加了一个新接口 Stringable
,也就是说任何实现 Stringable
接口的类应该实现 __toString(): string
魔术方法。
为提供前向兼容性,PHP 引擎自动将 Stringable
接口添加到所有实现 __toString()
方法的类中。
interface Stringable {
public function __toString(): string;
}
最常见的用例是在尝试使用字符串函数之前,在函数/方法内部的严格类型。
class Foo {
public function __toString(): string {
return 'FooBar';
}
}
function safePrint(string|Stringable $input): void {
echo htmlspecialchars((string) $input);
}
safePrint
函数可以使用联合类型指示或强制它支持标量 string
类型或者一个实现 Stringable
接口的类对象。
可选的声明 implements \Stringable
如果一个类实现了 __toString
方法,PHP会自动认为它实现了 Stringable
接口。它不是必需的,但可以显式声明它。
class Foo implements \Stringable{
public function __toString(): string {
return 'FooBar';
}
}
检测字符串功能
有了标量 string
类型和 Stringable
接口,现在可以在强制执行类型的应用程序中安全地使用字符串函数。
$foo instanceof \Stringable
实现 __toString
方法的类,无论是否带有 implements \Stringable
显式声明,instanceof
检测都会返回 true
。
class Foo {
public function __toString(): string {
return 'FooBar';
}
}
$foo = new Foo();
var_dump($foo instanceof Stringable); // true
请注意 instanceof
当前要求对象,暂且还不支持标量变量。
class_implements($foo)
class_implements
函数将正确返回具有 __toString()
方法的类中的给定对象,无论该方法带有或不带有显式 implements\Stringable
声明,该对象将返回 Stringable
作为该类实现的接口之一。
class Foo {
public function __toString(): string {
return 'FooBar';
}
}
var_dump(class_implements(new Foo()));
// array("Stringable" => "Stringable")
is_string($foo)
is_string
函数检测变量类型,只有提供的参数是 string
类型时,返回 true
。因为实现 __toString
的类对象是对象,is_string
会返回 false
,即使其符合 Stringable
接口。
class Foo {
public function __toString(): string {
return 'FooBar';
}
}
var_dump(is_string(new Foo())); // false
strict_types = 1
表现
当未强制执行严格类型时,可字符串对象将被计算为具有 __toString
的字符串。如果强制执行严格的类型(在文件顶部使用 declare(strict_types=1)
),则需要显式字符串强制转换。
declare(strict_types=1);
class Foo {
public function __toString(): string {
return 'FooBar';
}
}
function safePrint(string|Stringable $input): void {
echo htmlspecialchars($input);
//^^^ No string cast
}
safePrint(new Foo());
// Fatal error: Uncaught TypeError: htmlspecialchars(): Argument #1 ($string) must be of type string, Foo given in ...:...
在上面的代码段中,强制执行了严格的类型。htmlspecialchars
函数只接受字符串参数。如果传递了一个 Stringable
接口的对象,PHP 将拒绝接受它,即使该对象实现了 Stringable
接口。
严格类型启用时,确保将
string|Stringable
值使用(string)$input
进行类型转换。
向后兼容性影响
将新的 Stringable
接口引入 8.0 之前的 PHP 版本并不重要。但是,请注意,除非类明确声明 implements\Stringable
,否则8.0之前的PHP版本将不会提供如上所述的字符串功能检查。
interface Stringable {
public function __toString(): string;
}
class MyStringCapableClass implements Stringable {
public function __toString(): string {
return 'Hello World';
}
}
为了与以前的PHP版本兼容,必须添加 Stringable
类,并且所有提供字符串功能的类都必须明确声明它们 implements \Stringable
。
扩展阅读:PHP RFC: 添加 Stringable 接口
介绍
PHP 8 引入了 Stringable
接口,自动为实现 __toString()
方法的类添加该接口
有两个目标:
允许用 string|Stringable
类型来表达 string|object-with-__toString()[带有__toString()方法的对象]
为PHP7 升级到8 提供一个升级路径
目标1:允许在php8中使用 string|Stringable
联合类型, 用来接收字符串或者能够实现 __toString()
方法的对象。这是当前的一个重要缺失: 使用 Stringable
的对象编码时,没办法实现类型安全的。
实现 __toString()
方法的类,也可以 地声明接口。没有显式声明接口,也仍然隐式声明了。这让其可以同时向前兼容以及向后兼容: 使用polyfill , 类可以在 php7 中声明接口; 在 PHP 8 中, 类无需如此仍然可以匹配 string|Stringable
联合类型。
一旦 polyfill代码块被广泛使用(比如,成为 symfony/polyfill-php80 中的一部分 ), 当__toString()
被显式声明的时候,代码规范检查器可以强制声明接口,使之成为大家的习惯。
该接口声明的装代码如下:
interface Stringable
{
public function __toString(): string;
}
由于添加了返回类型,该接口会迫使现有想使用它的库拥有向后兼容的潜力。
为了让向前和向后兼容更容易,这个 RFC 同时建议如果 __toString()
方法没有显式返回类型,在编译时自动添加返回类型。如果在引擎上强制返回字符串,就不用在语法上做改动。
按照这种方式, 迁移到PHP8的代码,不必强制显式添加返回类型(向后兼容),PHP<8 的代码可以采用 pollfill 接口(为了兼容而不需要声明返回类型的接口)
提供简便的升级路径是本RFC的第二个目标。
…
原文查看https://wiki.php.net/rfc/stringable