PHP 7.4+ 类属性类型约束

5

我相信这个问题已经被问了很多次,但我似乎找不到一个好的/令人满意的答案,请耐心等待。

使用PHP 7.4+,我倾向于尽可能地进行类型声明。但是我在Doctrine实体属性方面遇到了一些问题。

如果我正确地进行了类型声明,通常会出现许多类似于以下错误的错误:

访问未初始化之前,类型为App\Entity\User::$createdAt的属性是不允许的

这种类型的错误的代码示例如下:

/**
 * @var DateTimeInterface
 * @ORM\Column(type="datetime")
 */
protected DateTimeInterface $createdAt;

我以前的做法是将属性设置为可为空,即使数据库字段不是这样。因此代码看起来可能像下面这样。

/**
 * @var DateTimeInterface|null
 * @ORM\Column(type="datetime")
 */
protected ?DateTimeInterface $createdAt = null;

现在我有另一个问题。我决定在我的项目中实现一个静态代码分析器,并且现在我正在使用PHPStan。因此,当我扫描我的代码时,会出现像上面一样的错误。


行 src/Entity/Trait/TimestampableEntityPropertiesTrait.php (在App\Entity\Article类的上下文中)


16 属性App\Entity\Article::$createdAt类型映射不匹配:属性可以包含DateTimeInterface|null,但数据库期望DateTimeInterface。


那么,处理这种类型的情况的正确方式是什么?

任何建议将不胜感激。

编辑

需要注意的是,有时我不想/无法在构造函数中初始化属性,因为我还没有正确的值。


你在构造函数中初始化属性吗?我希望phpstan能意识到这是在访问它之前。 - Barmar
@Barmar 我编辑了我的问题,但是不,这就是我的问题的重点。在我拥有的示例中,我正在处理一个日期时间“createdAt”,因此它并不重要,我可以在构造函数中初始化它。但是假设我有另一个日期时间(我还没有其值)或字符串或任何其他类型。我应该使用无效值进行初始化,并冒着将它们持久化的风险,仅因为它必须具有值吗? - Julien B.
这似乎是类型检查器的问题,如果它无法处理静态初始化变量为适当类型的事实。 - Barmar
2个回答

3

我不确定这是否是一种不好的做法,但结果证明我只需要从phpstan配置中删除该检查。

# phpstan.neon
parameters:
  doctrine:
    allowNullablePropertyForRequiredField: true

编辑:

经过一番探究,我意识到应该使用数据传输对象(DTO),这样可以允许空值,然后在准备好(且有效)之后将其转移到实体中。这样,我的实体始终是有效的,而且不会冒险将一些无效的数据刷新到数据库中。


而要求输入,然后允许空值(不要求它),这有什么意义呢?从一个几乎合乎逻辑的角度来看,可能可以两种方式都有 - 但两种方式结合起来似乎是自我打击的。 - Martin Zeitler
我只针对实体中的某些属性进行此操作,并且我希望使用类型来避免错误(例如在Datetime字段上设置字符串之类的错误)。 - Julien B.

1

phpstan 对代码异味的判断可能并不完全错误...
实际的Doctrine注解应该是 nullable=true

/**
 * @ORM\Column(type="datetime", nullable=true)
 */

然后ORM就不会再抱怨意外的NULL值了。
如果没有它,created_at将设置NOT NULL;因此它是“必需的”。
重点是,当它为nullable=true时,它就不再是必需的。
而当它不再是必需的时,phpstan也会停止抱怨。
另一方面,如果在应用程序级别上让phpstan忽略这些冲突,并使用allowNullablePropertyForRequiredField: true,则对底层数据库没有任何影响,底层数据库将根据用于生成数据库表的注释拒绝记录。

这是错误的,我不想在数据库中让数据可为空,我希望在 PHP 中它是“暂时可为空”的。 - Julien B.
这在带注释的模型中是不可能实现的;此外,拥有一个不能准确反映底层现实的抽象层是毫无意义的。你可能会将唯一可靠的方法标记为“错误”……因为这可能不是唯一的代码异味。正如所述,使用ORM可以两种方式都实现,但不能同时实现。请听phpstan的建议... - Martin Zeitler
除非该列为“可为空”,否则您必须手动“ALTER TABLE”,以便不删除记录...这违背了使用ORM的想法:拥有代码定义的数据源抽象。仍然可以在应用程序级别检查值是否存在。 - Martin Zeitler
如果有什么“问题”,那就是ORM模型无法在任何时候保存...这就是为什么phpstan正确报告了该问题的原因。忽视它不会让情况变得更好。 - Martin Zeitler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接