该扩展名提供以下功能:
findBy* , findOneBy*和countBy*方法。findBy findBy* , findOneBy , findOneBy* , count countBy*方法调用。EntityRepository<MyEntity>以进一步推断存储库中调用的方法。DoctrineORMEntityManager::getRepository()提供了正确的回报。DoctrineORMEntityManager::find , getReference和getPartialReference当Foo::class entity class名称作为第一个参数时DoctrineCommonCollectionsCollection上添加了缺少matching方法。可以通过设置parameters.doctrine.allCollectionsSelectable将其false 。allowNullablePropertyForRequiredField: true设置来放松这一点。DoctrineORMQuery::getResult , getOneOrNullResult ,getingleresult, getSingleResult , toIterable和在HYDRATE_OBJECT模式下execute返回类型(请参见下文)。 要使用此扩展名,请在作曲家中需要它:
composer require --dev phpstan/phpstan-doctrine如果您还安装了phpstan/Extension-installer,那么您将全部设置!
如果您不想使用phpstan/extension-installer ,请在项目的PHPSTAN配置中包含Extension.Neon:
includes :
- vendor/phpstan/phpstan-doctrine/extension.neon objectManagerLoader您对DQL/QueryBuilder验证感兴趣,请还包括rules.neon 。
includes :
- vendor/phpstan/phpstan-doctrine/rules.neon 如果您的存储库有一个公共基类,则可以在phpstan.neon和Phpstan将看到您在其中定义的其他方法:
parameters :
doctrine :
ormRepositoryClass : MyAppDoctrineBetterEntityRepository
odmRepositoryClass : MyAppDoctrineBetterDocumentRepository您可以通过从您自己的应用程序提供对象管理器来选择更高级的分析。这将启用DQL验证:
parameters :
doctrine :
objectManagerLoader : tests/object-manager.phpSymfony 4:
// tests/object-manager.php
use App Kernel ;
require __DIR__ . ' /../config/bootstrap.php ' ;
$ kernel = new Kernel ( $ _SERVER [ ' APP_ENV ' ], ( bool ) $ _SERVER [ ' APP_DEBUG ' ]);
$ kernel -> boot ();
return $ kernel -> getContainer ()-> get ( ' doctrine ' )-> getManager ();Symfony 5的示例:
// tests/object-manager.php
use App Kernel ;
use Symfony Component Dotenv Dotenv ;
require __DIR__ . ' /../vendor/autoload.php ' ;
( new Dotenv ())-> bootEnv ( __DIR__ . ' /../.env ' );
$ kernel = new Kernel ( $ _SERVER [ ' APP_ENV ' ], ( bool ) $ _SERVER [ ' APP_DEBUG ' ]);
$ kernel -> boot ();
return $ kernel -> getContainer ()-> get ( ' doctrine ' )-> getManager ();当提供objectManagerLoader时,该扩展可以推断DQL查询的结果类型。
示例:
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getResult (); // array<AcmeUser>
$ query = $ entityManager -> createQuery ( ' SELECT u.id, u.email, u.name FROM AcmeUser u ' );
$ query -> getResult (); // array<array{id: int, email: string, name: string|null}>
$ query = $ entityManager -> createQuery ( '
SELECT u.id, u.email, COALESCE(u.name, "Anonymous") AS name
FROM AcmeUser u
' );
$ query -> getSingleResult (Query:: HYDRATE_OBJECT ); // array{id: int, email: string, name: string}>
$ query = $ entityManager -> createQueryBuilder ()
-> select ( ' u ' )
-> from (User::class, ' u ' )
-> getQuery ();
$ query -> getResult (); // array<AcmeUser>查询在静态分析,不需要运行的数据库服务器。这利用了DQL解析器和实体元数据。
支持大多数DQL特征,包括GROUP BY , INDEX BY ,不同JOIN DISTINCT算术表达式mixed函数,聚合, NEW等。
EG SUM(e.column)是以float而获取, numeric-string还是int高度取决于驱动程序,其设置和PHP版本。此扩展可以自动检测您的设置,并为pdo_mysql , mysqli , pdo_sqlite , sqlite3 , pdo_pgsql和pgsql提供了非常准确的结果。
当无参数调用时,支持getResult方法,或将设置为Query::HYDRATE_OBJECT :
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getResult (); // array<User>
$ query -> getResult (Query:: HYDRATE_OBJECT ); // array<User>当hydrateMode参数明确toIterable getSingleResult Query::HYDRATE_OBJECT getOneOrNullResult hydrate_object execute
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getOneOrNullResult (); // mixed
$ query -> getOneOrNullResult (Query:: HYDRATE_OBJECT ); // User这是由于Query类的设计,除非在呼叫过程中明确指定这些功能,否则无法确定这些功能使用的水合模式。
并非每个查询构造器都可以静态分析,以下是最大化类型推断的建议:
select / join / from / set中)您可以通过以下方式报告不可用的地方。
parameters :
doctrine :
reportDynamicQueryBuilders : true 如果您的应用程序使用自定义类型,则可以编写自己的类型描述符以正确分析它们。类型描述符实现接口PHPStanTypeDoctrineDescriptorsDoctrineTypeDescriptor该词看起来像这样:
<?php
public function getType(): string ;
public function getWritableToPropertyType(): Type ;
public function getWritableToDatabaseType(): Type ;getType()方法只需返回自定义类型的类名。getWritableToPropertyType()方法返回自定义类型将写入实体属性字段的phpstan类型。基本上,这是自定义类型的convertToPHPValue()方法的返回类型。getWritableToDatabaseType()方法将可以从实体的属性字段写入自定义类型的PHPSTAN类型。同样,基本上,这是自定义类型的convertToDatabaseValue()的第一个参数的允许类型。通常,至少对于大多数学说的本地类型,这些最后两种方法将返回相同的类型,但并非总是如此。一个示例是datetime类型,它允许您将任何DateTimeInterface设置为属性字段,但是从数据库加载时始终包含DateTime类型。
类型的描述符不必处理无效的类型,因为根据需要,这些类型可以从描述符类型中透明/删除。因此,即使您的自定义类型允许null ,您也不必从描述符的方法返回自定义类型的联合类型和NullType 。
如果您的自定义类型的convertToPHPValue()和convertToDatabaseValue()方法具有适当的typeHints,则不必为此编写自己的描述符。 PHPStanTypeDoctrineDescriptorsReflectionDescriptor可以分析typehints并为您完成其余部分。
如果您的类型的父级是该学说的非提取者之一,那么ReflectionDescriptor即使将其描述符重复使用以表达式解决方案(例如AVG(t.cost) )。例如,如果您扩展了DoctrineDBALTypesDecimalType ,它将知道SQLite将其作为float|int和其他驱动程序作为numeric-string 。如果仅扩展了DoctrineDBALTypesType ,则应使用自定义描述符,并选择实现DoctrineTypeDriverAwareDescriptor以提供特定于驱动程序的分辨率。
当您编写自定义类型描述符时,您必须让Phpstan知道。将类似的东西添加到您的phpstan.neon中:
services :
-
class : MyCustomTypeDescriptor
tags : [ phpstan.doctrine.typeDescriptor ]
# in case you are using the ReflectionDescriptor
-
factory : PHPStanTypeDoctrineDescriptorsReflectionDescriptor ( ' MyAppMyCustomTypeName ' )
tags : [ phpstan.doctrine.typeDescriptor ]如果您想确保永远不会忘记某种自定义类型的描述符,则可以启用:
parameters :
doctrine :
reportUnknownTypes : true当您的实体使用无描述符的自定义类型时,这会导致失败:
#[Entity]
abstract class Uuid7Entity
{
#[Id]
#[Column(type: Uuid7Type:: NAME )] // reported when descriptor for such type is missing
private Uuid7 $ hsCode ;实现学说的TypedExpression表达的任何自定义DQL函数都可以通过此扩展来理解,并使用其getReturnType()方法中使用的类型来推断。所有其他自定义DQL功能都被推断为mixed 。请注意,您不能使用本机StringType来铸造(和推断)字符串结果(请参见ORM问题)。
use Doctrine DBAL Types Type ;
use Doctrine DBAL Types Types ;
use Doctrine ORM Query AST TypedExpression ;
use Doctrine ORM Query AST Functions FunctionNode ;
use Doctrine ORM Query Parser ;
use Doctrine ORM Query SqlWalker ;
use Doctrine ORM Query TokenType ;
class Floor extends FunctionNode implements TypedExpression
{
private AST Node | string $ arithmeticExpression ;
public function getSql ( SqlWalker $ sqlWalker ): string
{
return ' FLOOR( ' . $ sqlWalker -> walkSimpleArithmeticExpression ( $ this -> arithmeticExpression ) . ' ) ' ;
}
public function parse ( Parser $ parser ): void
{
$ parser -> match (TokenType:: T_IDENTIFIER );
$ parser -> match (TokenType:: T_OPEN_PARENTHESIS );
$ this -> arithmeticExpression = $ parser -> SimpleArithmeticExpression ();
$ parser -> match (TokenType:: T_CLOSE_PARENTHESIS );
}
public function getReturnType (): Type
{
return Type:: getType (Types:: INTEGER );
}
}phpstan-doctrine中的存根文件带有许多参数,上面标有literal-string 。这是一种以安全性为中心的类型,仅允许用代码编写的文字字符串传递到这些参数中。
这降低了SQL注入的风险,因为从用户输入的动态字符串不被代替literal-string 。
使用此类型的一个示例是DoctrineDbalConnection::executeQuery()中的$sql参数。
要启用Phpstan-Doctrine中的高级类型,请使用此配置参数:
parameters :
doctrine :
literalString : true