Cette extension fournit des fonctionnalités suivantes:
findBy* , findOneBy* et countBy* Méthodes sur EntityRepository.findBy , findBy* , findOneBy , findOneBy* , count et countBy* Méthode.EntityRepository<MyEntity> correctement dans les PHPDOC pour une inférence supplémentaire des méthodes appelées sur le référentiel.DoctrineORMEntityManager::getRepository() .DoctrineORMEntityManager::find , getReference et getPartialReference lorsque le nom de la classe Foo::class Entité de classe est fourni comme premier argumentmatching manquante sur DoctrineCommonCollectionsCollection . Cela peut être désactivé en définissant parameters.doctrine.allCollectionsSelectable à false .allowNullablePropertyForRequiredField: true Setting.DoctrineORMQuery::getResult , getOneOrNullResult , getSingleResult , toIterable et execute en mode HYDRATE_OBJECT (voir ci-dessous). Pour utiliser cette extension, nécessitez-le dans le compositeur:
composer require --dev phpstan/phpstan-doctrineSi vous installez également PhpStan / Extension-Installer, vous êtes tous réglés!
Si vous ne souhaitez pas utiliser phpstan/extension-installer , incluez l'extension.neon dans la configuration phpstan de votre projet:
includes :
- vendor/phpstan/phpstan-doctrine/extension.neon Si vous êtes intéressé par la validation DQL / QueryBuilder, incluez également rules.neon (vous devrez également fournir le objectManagerLoader , voir ci-dessous):
includes :
- vendor/phpstan/phpstan-doctrine/rules.neon Si vos référentiels ont une classe de base commune, vous pouvez la configurer dans votre phpstan.neon et phpstan verront des méthodes supplémentaires que vous y définissez:
parameters :
doctrine :
ormRepositoryClass : MyAppDoctrineBetterEntityRepository
odmRepositoryClass : MyAppDoctrineBetterDocumentRepositoryVous pouvez opter pour une analyse plus avancée en fournissant le gestionnaire d'objets à partir de votre propre application. Cela permettra la validation DQL:
parameters :
doctrine :
objectManagerLoader : tests/object-manager.phpExemple pour Symfony 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 ();Exemple pour 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 (); Cette extension peut déduire le type de résultat des requêtes DQL lorsqu'un objectManagerLoader est fourni.
Exemples:
$ 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>Les requêtes sont analysées statiquement et ne nécessitent pas de serveur de base de données en cours. Cela utilise les métadonnées DQL Parser et Entités DQL.
La plupart des fonctionnalités DQL sont prises en charge, y compris GROUP BY , INDEX BY , DISTINCT , toutes les saveurs de JOIN , les expressions arithmétiques, les fonctions, les agrégations, NEW , etc. Les sous-requêtes ne sont pas encore prises en charge (le type induit sera mixed ).
Que SUM(e.column) soit récupérée en float , numeric-string ou int dépend fortement des pilotes, de leur configuration et de leur version PHP. Cette extension automatiquement votre configuration et fournit des résultats assez précis pour pdo_mysql , mysqli , pdo_sqlite , sqlite3 , pdo_pgsql et pgsql .
La méthode getResult est prise en charge lorsqu'elle est appelée sans argument, ou avec l'argument HydrateMode défini sur Query::HYDRATE_OBJECT :
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getResult (); // array<User>
$ query -> getResult (Query:: HYDRATE_OBJECT ); // array<User> Les méthodes getOneOrNullResult , getSingleResult , toIterable et execute sont prises en charge lorsque l'argument hydratemode est explicitement défini sur Query::HYDRATE_OBJECT :
$ query = $ entityManager -> createQuery ( ' SELECT u FROM AcmeUser u ' );
$ query -> getOneOrNullResult (); // mixed
$ query -> getOneOrNullResult (Query:: HYDRATE_OBJECT ); // User Cela est dû à la conception de la classe Query empêchant de déterminer le mode d'hydratation utilisé par ces fonctions, sauf s'il est spécifié explicitement pendant l'appel.
Tous les bobines de requête ne peuvent pas être analysées statiquement, voici quelques conseils pour maximiser le type inférieur:
select / join / from / set )Vous pouvez permettre la déclaration des endroits où l'inférences n'est pas disponible en:
parameters :
doctrine :
reportDynamicQueryBuilders : true Si votre application utilise des types de doctrine personnalisés, vous pouvez écrire vos propres descripteurs de type pour les analyser correctement. Type Descriptors Implémentez l'interface PHPStanTypeDoctrineDescriptorsDoctrineTypeDescriptor qui ressemble à ceci:
<?php
public function getType(): string ;
public function getWritableToPropertyType(): Type ;
public function getWritableToDatabaseType(): Type ;getType() renvoie simplement le nom de classe du type personnalisé.getWritableToPropertyType() renvoie le type PHPSTAN que le type personnalisé rédigera dans le champ de propriété de l'entité. Fondamentalement, il s'agit du type de retour de la méthode convertToPHPValue() du type personnalisé.getWritableToDatabaseType() renvoie le type phpstan qui peut être écrit à partir du champ de propriété de l'entité dans le type personnalisé. Encore une fois, c'est le type autorisé pour le premier argument du type convertToDatabaseValue() du type personnalisé. Généralement, au moins pour la plupart des types natifs de la doctrine, ces deux dernières méthodes rendront le même type, mais ce n'est pas toujours le cas. Un exemple serait le type datetime , qui vous permet de définir n'importe quelle DateTimeInterface dans le champ de propriété, mais contiendra toujours le type DateTime lorsqu'il est chargé à partir de la base de données.
Les descripteurs de type n'ont pas à gérer les types nullables, car ceux-ci sont ajoutés / supprimés de manière transparente des types du descripteur selon les besoins. Par conséquent, vous n'avez pas à retourner le type syndical de votre type personnalisé et NullType à partir des méthodes du descripteur, même si votre type personnalisé permet null .
Si convertToPHPValue() et les méthodes convertToDatabaseValue() de votre type, ont des forces de type appropriées, vous n'avez pas à écrire votre propre descripteur pour cela. Le PHPStanTypeDoctrineDescriptorsReflectionDescriptor peut analyser les types de type et faire le reste pour vous.
Si le parent de votre type est l'un des non-abstraits de la doctrine, ReflectionDescriptor réutilisera son descripteur même pour la résolution d'expression (par exemple AVG(t.cost) ). Par exemple, si vous étendez DoctrineDBALTypesDecimalType , il saura que Sqlite récupère cela comme float|int et d'autres pilotes comme numeric-string . Si vous n'étendez que DoctrineDBALTypesType , vous devez utiliser le descripteur personnalisé et implémenter éventuellement même DoctrineTypeDriverAwareDescriptor pour fournir une résolution spécifique au pilote.
Lorsque vous écrivez un descripteur de type personnalisé, vous devez en informer PHPSTAN. Ajoutez quelque chose comme ça dans votre phpstan.neon :
services :
-
class : MyCustomTypeDescriptor
tags : [ phpstan.doctrine.typeDescriptor ]
# in case you are using the ReflectionDescriptor
-
factory : PHPStanTypeDoctrineDescriptorsReflectionDescriptor ( ' MyAppMyCustomTypeName ' )
tags : [ phpstan.doctrine.typeDescriptor ]Si vous souhaitez être sûr de ne jamais oublier le descripteur pour un type personnalisé, vous pouvez activer:
parameters :
doctrine :
reportUnknownTypes : trueCela provoque des échecs lorsque votre entité utilise un type personnalisé sans descripteur:
#[Entity]
abstract class Uuid7Entity
{
#[Id]
#[Column(type: Uuid7Type:: NAME )] // reported when descriptor for such type is missing
private Uuid7 $ hsCode ; Toute fonction DQL personnalisée qui TypedExpression la doctrine est comprise par la doctrine et est déduite avec le type utilisé dans sa méthode getReturnType() . Toutes les autres fonctions DQL personnalisées sont déduites comme mixed . Veuillez noter que vous ne pouvez pas utiliser StringType natif pour lancer (et déduire) les résultats de la chaîne (voir le problème 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 );
}
} Les fichiers Stub dans Phpstan-doctrine sont livrés avec de nombreux paramètres marqués de literal-string . Il s'agit d'un type axé sur la sécurité qui permet uniquement par les chaînes littérales écrites en code dans ces paramètres.
Cela réduit le risque d'injection SQL car les chaînes dynamiques de l'entrée de l'utilisateur ne sont pas acceptées à la place de literal-string .
Un exemple où ce type est utilisé est $sql dans DoctrineDbalConnection::executeQuery() .
Pour activer ce type avancé en phpstan-doctrine, utilisez ce paramètre de configuration:
parameters :
doctrine :
literalString : true