是否可以输入不止一种类型的提示?
Posted
技术标签:
【中文标题】是否可以输入不止一种类型的提示?【英文标题】:Is it possible to type hint more than one type? 【发布时间】:2018-03-28 06:43:22 【问题描述】:我可以使用类型提示允许两种不同的类型吗?
例如参数$requester
可以是User
或File
:
function log (User|File $requester)
【问题讨论】:
如果两个类有一个共同的继承或接口(例如Loggable
),那么你可以使用它作为类型提示
Is it possible to specify more than one type hint for a parameter?的可能重复
【参考方案1】:
从 php 8.0 开始,这将成为可能,包括联合类型。
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 /** **/
【讨论】:
你能输入 PHP 8 的异常吗? 你为什么不能呢?例外是类。 我的意思是输入一个方法可能会抛出异常——而不是返回它。myMethod(): void|MyException
不起作用
这是一个 return 类型提示。你所说的语法是“此方法返回void
或MyException
的实例。没有“可能引发异常”的语法,您需要使用注释或属性。【参考方案2】:
在学术上,这称为type union。
PHP 中的联合类型
如其他答案中所述,您可以通过创建接口、父类型等来作弊,但是除了增加项目的复杂性和 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 ...
PHP(几乎)有联合类型的那一天
2015 年 2 月 14 日,Union Types PHP RFC 被提议用于 PHP 7.1。经过讨论和投票,18个“否”对11个“是”被否决。
如果 RFC 已被接受,PHP 将拥有与您显示的完全相同的联合类型 (User|File
)。
RFC 有一些缺陷,但它被拒绝的主要原因是 mainteners 投票者非常抗拒改变,尤其是在涉及类型严格性和其他编程范式时(例如 “当默认值采用所有类型的值时,为什么我们需要类型联合”和“这对性能不利”)。
【讨论】:
我有点不相信,但我只是检查了一下,很遗憾地去看了 RFC 来思考它,然后决定去看看 RFC 索引,和 UNION TYPES V2 位于 RFC 列表的顶部。哇。它从字面上开始讨论 6 天前。 github.com/php/php-rfcs/pull/1 - 这也是第一个在 GH 上讨论的 RFC,甚至。迷人的时间大声笑 @i336_ 是的,我也认为它会与这个战斗 - wiki.php.net/rfc/mixed-typehint 我反复遇到的联合类型的主要需求之一是 ArrayAccess 不允许传递数组。我从来没有找到一个很好的解决方案来解决这个问题,默认情况下在这些情况下根本没有任何类型提示。 RFC for Union Types 在 PHP 8 中被接受和实现。【参考方案3】:目前在 PHP 中是不可能的。但是,您可以拥有一个interface
,并为User
和File
实现它,然后将该接口用作log()
中的类型提示:
<?php
interface UserFile
class User implements UserFile
class File implements UserFile
// snip
public function log (UserFile $requester)
【讨论】:
【参考方案4】:您可以检查函数内部的类型。
function log ($requester)
if ($requester instanceof User || $requester instanceof File)
//Do your job
【讨论】:
【参考方案5】:或者您可以为每个方法设置 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();
【讨论】:
【参考方案6】:您可以为这两个创建父类:
abstract class Parent_class
class User extends Parent_class
// ...
class File extends Parent_class
// ...
并在函数中使用
function log (Parent_class $requester)
// ... code
【讨论】:
以上是关于是否可以输入不止一种类型的提示?的主要内容,如果未能解决你的问题,请参考以下文章