在 PhpStan 中使用枚举作为 PHP 中的字符串
Using Enum as string in PHP with PhpStan
我正在 Symfony 6 应用程序中试验 PHP 枚举,我认为我找到了一个非常好的用例。一切正常,但 phpstan 一直抱怨类型 I return.
------ -----------------------------------------------------------------------------------------------------------------------------------------------
Line src/Entity/User.php
------ -----------------------------------------------------------------------------------------------------------------------------------------------
93 Return type (array<App\Security\Roles>) of method App\Entity\User::getRoles() should be compatible with return type (array<string>) of method
Symfony\Component\Security\Core\User\UserInterface::getRoles()
------ -----------------------------------------------------------------------------------------------------------------------------------------------
我的枚举如下所示:
namespace App\Security;
enum Roles: string
{
case Admin = 'ROLE_ADMIN';
case User = 'ROLE_USER';
}
在我实现 Symfony\Component\Security\Core\User\UserInterface
的 User
实体中,我有这个:
/**
* @see UserInterface
* @return array<Roles>
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = Roles::User->value;
return array_unique($roles);
}
/**
* @param array<Roles> $roles
*/
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
我知道 phpstan 期望数组包含接口中定义的字符串,
/**
* @return string[]
*/
public function getRoles(): array;
但是有办法解决这个问题吗?我真的更愿意能够利用枚举。向枚举添加类型不是为了能够将其用作该类型吗?另外值得一提的是,我到处都使用严格的类型,除了 phpstan 对我的类型不太满意之外,一切正常。
您的 return 注释是错误的,因为 PhpStan 的报告是正确的。
你说你报告了一组角色:
/**
* @return array<Roles>
*/
如果是这种情况,roles
将直接包含枚举,而不是支持枚举的值:
$this->roles[] = Roles::Admin
通过保留错误的注释,您不仅让 PhpStan 感到不安,而且还向 getRoles()
的消费者提供了错误的信息。你的注释意味着我应该能够做到:
/** @var <array>Roles */
$roles = $user->getRoles();
foreach ($roles as $role) {
if ($role === Roles::Admin) {
// something
}
}
但您实际上是在 returning string[]
,而不是 Roles[]
,所以我的代码无法运行,我的应用程序会崩溃。悲伤遍地。
没有看到额外的代码,看起来您并没有真正以不同于 class 常量的方式“使用枚举”。 roles
不是 Roles
的集合,您的 addRole()
方法需要 string
,而不是 Roles
,等等
当然很好。由于您需要遵守 UserInterface
,因此带有一些常量的 class 可能更适合这里。
/**
* @return array<Roles::ROLE_*>
*/
此注释工作的一个简单示例是:
<?php declare(strict_types = 1);
abstract class Roles {
const ROLE_FOO = 'foo';
const ROLE_BAR = 'bar';
const ROLE_BAZ = 'baz';
}
/**
* @param array<Roles::*> $s
*/
function sayHello(array $s): void
{
foreach ($s as $si) {
echo 'Hello, ' . $si, "\n";
}
}
$array = [
Roles::ROLE_FOO,
Roles::ROLE_BAR
];
sayHello($array);
$badArray = [
Roles::ROLE_FOO,
'some'
];
// this is an error!
sayHello($badArray);
如果你想为你的角色使用枚举,那么就实际使用它们。但是你不能直接在 getRoles()
中使用它们,因为它会破坏界面。
您可以使用一组配套方法(getRealRoles()
、addRealRole(Roles $role)
等)来处理 roles
属性,然后您将离开getRoles()
只是将 array<Roles>
转换为 array<string>
.
不,你不能表达“getRoles
returns 一个由元素组成的数组,这些元素是 Roles
枚举的可能支持值”,但它是很好,因为在你的(与不关心你的实现的框架代码相反)代码中你会使用与枚举一起工作的方法。
我正在 Symfony 6 应用程序中试验 PHP 枚举,我认为我找到了一个非常好的用例。一切正常,但 phpstan 一直抱怨类型 I return.
------ -----------------------------------------------------------------------------------------------------------------------------------------------
Line src/Entity/User.php
------ -----------------------------------------------------------------------------------------------------------------------------------------------
93 Return type (array<App\Security\Roles>) of method App\Entity\User::getRoles() should be compatible with return type (array<string>) of method
Symfony\Component\Security\Core\User\UserInterface::getRoles()
------ -----------------------------------------------------------------------------------------------------------------------------------------------
我的枚举如下所示:
namespace App\Security;
enum Roles: string
{
case Admin = 'ROLE_ADMIN';
case User = 'ROLE_USER';
}
在我实现 Symfony\Component\Security\Core\User\UserInterface
的 User
实体中,我有这个:
/**
* @see UserInterface
* @return array<Roles>
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = Roles::User->value;
return array_unique($roles);
}
/**
* @param array<Roles> $roles
*/
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
我知道 phpstan 期望数组包含接口中定义的字符串,
/**
* @return string[]
*/
public function getRoles(): array;
但是有办法解决这个问题吗?我真的更愿意能够利用枚举。向枚举添加类型不是为了能够将其用作该类型吗?另外值得一提的是,我到处都使用严格的类型,除了 phpstan 对我的类型不太满意之外,一切正常。
您的 return 注释是错误的,因为 PhpStan 的报告是正确的。
你说你报告了一组角色:
/**
* @return array<Roles>
*/
如果是这种情况,roles
将直接包含枚举,而不是支持枚举的值:
$this->roles[] = Roles::Admin
通过保留错误的注释,您不仅让 PhpStan 感到不安,而且还向 getRoles()
的消费者提供了错误的信息。你的注释意味着我应该能够做到:
/** @var <array>Roles */
$roles = $user->getRoles();
foreach ($roles as $role) {
if ($role === Roles::Admin) {
// something
}
}
但您实际上是在 returning string[]
,而不是 Roles[]
,所以我的代码无法运行,我的应用程序会崩溃。悲伤遍地。
没有看到额外的代码,看起来您并没有真正以不同于 class 常量的方式“使用枚举”。 roles
不是 Roles
的集合,您的 addRole()
方法需要 string
,而不是 Roles
,等等
当然很好。由于您需要遵守 UserInterface
,因此带有一些常量的 class 可能更适合这里。
/**
* @return array<Roles::ROLE_*>
*/
此注释工作的一个简单示例是:
<?php declare(strict_types = 1);
abstract class Roles {
const ROLE_FOO = 'foo';
const ROLE_BAR = 'bar';
const ROLE_BAZ = 'baz';
}
/**
* @param array<Roles::*> $s
*/
function sayHello(array $s): void
{
foreach ($s as $si) {
echo 'Hello, ' . $si, "\n";
}
}
$array = [
Roles::ROLE_FOO,
Roles::ROLE_BAR
];
sayHello($array);
$badArray = [
Roles::ROLE_FOO,
'some'
];
// this is an error!
sayHello($badArray);
如果你想为你的角色使用枚举,那么就实际使用它们。但是你不能直接在 getRoles()
中使用它们,因为它会破坏界面。
您可以使用一组配套方法(getRealRoles()
、addRealRole(Roles $role)
等)来处理 roles
属性,然后您将离开getRoles()
只是将 array<Roles>
转换为 array<string>
.
不,你不能表达“getRoles
returns 一个由元素组成的数组,这些元素是 Roles
枚举的可能支持值”,但它是很好,因为在你的(与不关心你的实现的框架代码相反)代码中你会使用与枚举一起工作的方法。