Ulr*_*rdt 5 php type-safety php-7
我的代码看起来像这样,我想改进它:\n \n\n
// example type\nclass Stuff\n{\n public function __construct($name)\n {\n $this->name = $name;\n }\n\n public function getName()\n {\n return $this->name;\n }\n}\n\n// generator function\nfunction searchStuff()\n{\n yield new Stuff(\'Fou\');\n yield new Stuff(\'Barre\');\n yield new Stuff(\'Bazze\');\n}\n\n// code that iterates over the results of the generator\n$stuffIterator = searchStuff();\nassert($stuffIterator instanceof Iterator);\nforeach ($stuffIterator as $stuff) {\n /** @var Stuff $stuff */\n echo $stuff->getName() . PHP_EOL;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n我想要改进的是循环中的注释(倒数第三行),我想完全删除它。原因是
\n\n我的 na\xc3\xafve 方法是声明一个迭代器接口,该接口向通用Iterator接口添加适当的类型注释:
interface StuffIterator extends Iterator\n{\n public function current(): Stuff;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这有一个缺点,我不能将其设置为函数上的“硬”注释,只能作为文档字符串注释,因为"Generators may only declare a return type of Generator, Iterator, Traversable, or iterable",这是不好的,因为这样就不会强制执行。此外,我的 IDE 无法识别该类型,但这是一个不同的问题。
另一种方法是编写一个实际的迭代器类来包装Generator函数的返回值。问题是这个类也需要实例化,所以我必须调用$stuffGenerator = new StuffIterator(searchStuff());或编写另一个包装函数来执行此操作,这两者都不是必需的。尽管如此,愚蠢的 IDE 仍然无法识别类型提示(grrrrr...!)。
所以,这是我的问题:这种方法还有哪些替代方案?我想像 C++ 或 Java 泛型之类的东西,但可惜,我不能简单地重写有问题的应用程序。
\n\n进一步说明:
\n\n这是一个很好的问题。我想你的问题的答案不会像你想象的那样。该解决方案可能不太好,但有效。首先,您不能定义除等之外的收益率返回类型Generator。你自己已经给出了答案。但 ...
只需想象以下起点即可。
class Stuff
{
protected $name;
public function getName() : ?string
{
return $this->name;
}
public function setName(string $name) : Stuff
{
$this->name = $name;
return $this;
}
}
class StuffCollection extends \IteratorIterator
{
public function __construct(Stuff ...$items)
{
parent::__construct(
(function() use ($items) {
yield from $items;
})()
);
}
public function current() : Stuff
{
return parent::current();
}
}
Run Code Online (Sandbox Code Playgroud)
我在这里做了什么?我们Stuff已经知道这个班级了。它没有什么新的作用。新事物是StuffCollection班级。由于从IteratorIterator类扩展它,我们可以重写该IteratorIterator::current()方法并为其提供类型提示。
$collection = new StuffCollection(
(new Stuff())->setName('One'),
(new Stuff())->setName('Two'),
(new Stuff())->setName('Three')
);
foreach ($collection as $item) {
var_dump(assert($item instance of Stuff));
echo sprintf(
'Class: %s. Calling getName method returns "%s" (%s)',
get_class($item),
$item->getName(),
gettype($item->getName())
) . "<br>";
}
Run Code Online (Sandbox Code Playgroud)
其输出应该是......
bool(true) Class: Stuff. Calling getName method returns "One" (string)
bool(true) Class: Stuff. Calling getName method returns "Two" (string)
bool(true) Class: Stuff. Calling getName method returns "Three" (string)
Run Code Online (Sandbox Code Playgroud)
这意味着什么?您确实不能直接在yield 调用中定义返回类型。yield 总是会返回一个Generator实例。一种可能的解决方案是使用IteratorIterator。
即使您的 IDE 也应该适用于该解决方案。