【发布时间】:2018-03-28 06:43:22
【问题描述】:
我可以使用类型提示允许两种不同的类型吗?
例如参数$requester 可以是User 或File:
function log (User|File $requester) {
}
【问题讨论】:
-
如果两个类有一个共同的继承或接口(例如
Loggable),那么你可以使用它作为类型提示
标签: php type-hinting php-8
我可以使用类型提示允许两种不同的类型吗?
例如参数$requester 可以是User 或File:
function log (User|File $requester) {
}
【问题讨论】:
Loggable),那么你可以使用它作为类型提示
标签: php type-hinting php-8
proposal has been voted 61 in favour to 5 against,实施已准备就绪。
有一个previous RFC 提出这个建议,在另一个答案中提到,但最终被拒绝了。
它将与您问题中的示例完全相同:
class F
{
public function foo (File|Resource $f) : int|float { /** implement this**// }
}
这意味着F::foo() 需要File 或资源,并将返回int 或float。
另外几点:
此外,您可以使用null 声明联合。 A|null 等价于 ?A,但更复杂的声明为 A|B|null 也是可能的。
也可以使用false 类型作为联合类型声明的一部分。例如。 int|false。这主要是由于历史原因,因为某些内部函数在某些类型的错误条件下返回false。以strpos() 为例。
在这些情况下,更现代的函数可能应该返回 null 或引发异常,但包含此替代方案是为了解决遗留代码。
添加联合类型用于参数类型提示(从而减少函数限制)和删除联合类型用于返回类型提示(使返回类型更具体)。
鉴于上面的 F 类,这是合法的:
class G extends F
{
public function foo(File|Resource|string $f) : int { /** **/ }
}
但这不是:
class H extends F
{
public function foo(File $f) : int|float|bool { /** **/ }
}
【讨论】:
myMethod(): void|MyException 不起作用
void 或MyException 的实例。没有“可能引发异常”的语法,您需要使用注释或属性。
在学术上,这称为type union。
如其他答案中所述,您可以通过创建接口、父类型等来作弊,但是除了增加项目的复杂性和 LoC 之外,还有什么意义呢?另外,这不适用于标量类型,因为您无法扩展/实现标量类型。
您会得到相反的结果,而不是使代码更具可读性。除非那些类/接口已经存在并且它们是因为 OOP 而存在,而不是为了解决类型提示问题。
PHP 中的规范答案是……好吧,只是不要输入类型提示。该语言不被认为具有复杂而强大的类型系统,试图解决该语言的缺陷并不是一个好的答案。
相反,正确记录您的函数:
/**
* Description of what the function does.
*
* @param User|File $multiTypeArgument Description of the argument.
*
* @return string[] Description of the function's return value.
*/
function myFunction($multiTypeArgument)
{
这至少会为自动完成和静态代码分析带来 IDE 支持。在从事私人项目、网站等工作时就足够了。
在设计公共 API(PHP 库等)时,有时您可能希望对 API 使用者的输入更具防御性。
那么@tilz0R 的答案就是要走的路:
function log($message) {
if (!is_string($message) && !$message instanceof Message) {
throw new \InvalidArgumentException('$message must be a string or a Message object.');
}
// code ...
}
2015 年 2 月 14 日,Union Types PHP RFC 被提议用于 PHP 7.1。经过讨论和投票,18个“否”对11个“是”被否决。
如果 RFC 已被接受,PHP 将拥有与您显示的完全相同的联合类型 (User|File)。
RFC 有一些缺陷,但它被拒绝的主要原因是 mainteners 投票者非常抗拒改变,尤其是在涉及类型严格性和其他编程范式时(例如 “当默认值采用所有类型的值时,为什么我们需要类型联合”和“这对性能不利”)。
【讨论】:
目前在 PHP 中是不可能的。但是,您可以拥有一个interface,并为User 和File 实现它,然后将该接口用作log() 中的类型提示:
<?php
interface UserFile {
}
class User implements UserFile {
}
class File implements UserFile {
}
// snip
public function log (UserFile $requester) {
}
【讨论】:
您可以检查函数内部的类型。
function log ($requester) {
if ($requester instanceof User || $requester instanceof File) {
//Do your job
}
}
【讨论】:
或者您可以为每个方法设置 2 种方法和一种动态方法:
function logUser($requester)
{
//
}
function logFile($requester)
{
//
}
还有动态的
function log($requester)
{
if ($requester instanceof File) {
return $this->logFile($requester);
}
if ($requester instanceof User) {
return $this->logUser($requester);
}
throw new LogMethodException();
}
【讨论】:
您可以为这两个创建父类:
abstract class Parent_class {}
class User extends Parent_class {
// ...
}
class File extends Parent_class {
// ...
}
并在函数中使用
function log (Parent_class $requester) {
// ... code
}
【讨论】: