diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c7d83123..edf69316 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 1 + level: 3 paths: - src - tests @@ -7,3 +7,14 @@ parameters: excludePaths: - src/Command/Proxy/ConvertMappingDoctrineCommand.php - src/Command/Proxy/EnsureProductionSettingsDoctrineCommand.php + ignoreErrors: + # Available in ORM < 3 only + - '#Doctrine\\ORM\\Tools\\EntityGenerator.#' + - '#Doctrine\\ORM\\Tools\\DisconnectedClassMetadataFactory.#' + - '#Doctrine\\ORM\\Tools\\Export\\ClassMetadataExporter.#' + # phpstan has no array shape intersection support https://github.com/phpstan/phpstan/issues/12414 + - message: '#unresolvable type.#' + path: src/DataCollector/DoctrineDataCollector.php + # Probably needs Symfony plugin + - message: '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\Node#' + path: src/DependencyInjection/Configuration.php diff --git a/src/Command/DoctrineCommand.php b/src/Command/DoctrineCommand.php index da787f7a..056e4eda 100644 --- a/src/Command/DoctrineCommand.php +++ b/src/Command/DoctrineCommand.php @@ -3,12 +3,14 @@ namespace Doctrine\Bundle\DoctrineBundle\Command; use Doctrine\DBAL\Connection; -use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Tools\EntityGenerator; use Doctrine\Persistence\ManagerRegistry; use InvalidArgumentException; use Symfony\Component\Console\Command\Command; +use function assert; + /** * Base class for Doctrine console commands to extend from. * @@ -29,12 +31,9 @@ public function __construct(ManagerRegistry $doctrine) * get a doctrine entity generator * * @return EntityGenerator - * - * @psalm-suppress UndefinedDocblockClass ORM < 3 specific */ protected function getEntityGenerator() { - /** @phpstan-ignore class.notFound */ $entityGenerator = new EntityGenerator(); $entityGenerator->setGenerateAnnotations(false); $entityGenerator->setGenerateStubMethods(true); @@ -52,7 +51,7 @@ protected function getEntityGenerator() * @param string $name * @param int|null $shardId * - * @return EntityManager + * @return EntityManagerInterface */ protected function getEntityManager($name, $shardId = null) { @@ -62,6 +61,8 @@ protected function getEntityManager($name, $shardId = null) throw new InvalidArgumentException('Shards are not supported anymore using doctrine/dbal >= 3'); } + assert($manager instanceof EntityManagerInterface); + return $manager; } diff --git a/src/Command/ImportMappingDoctrineCommand.php b/src/Command/ImportMappingDoctrineCommand.php index 44ca4465..28a68d8a 100644 --- a/src/Command/ImportMappingDoctrineCommand.php +++ b/src/Command/ImportMappingDoctrineCommand.php @@ -2,6 +2,7 @@ namespace Doctrine\Bundle\DoctrineBundle\Command; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Driver\DatabaseDriver; use Doctrine\ORM\Tools\Console\MetadataFilter; use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; @@ -13,6 +14,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use function assert; use function chmod; use function dirname; use function file_put_contents; @@ -91,6 +93,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $namespaceOrBundle = $input->getArgument('name'); if (isset($this->bundles[$namespaceOrBundle])) { + /** @phpstan-ignore method.notFound */ $bundle = $this->getApplication()->getKernel()->getBundle($namespaceOrBundle); $namespace = $bundle->getNamespace() . '\Entity'; @@ -121,13 +124,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int $em = $this->getEntityManager($input->getOption('em')); + /* @phpstan-ignore method.notFound (Available in DBAL < 4) */ $databaseDriver = new DatabaseDriver($em->getConnection()->getSchemaManager()); $em->getConfiguration()->setMetadataDriverImpl($databaseDriver); $emName = $input->getOption('em'); $emName = $emName ? $emName : 'default'; - /* @phpstan-ignore class.notFound */ $cmf = new DisconnectedClassMetadataFactory(); $cmf->setEntityManager($em); $metadata = $cmf->getAllMetadata(); @@ -135,6 +138,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($metadata) { $output->writeln(sprintf('Importing mapping information from "%s" entity manager', $emName)); foreach ($metadata as $class) { + assert($class instanceof ClassMetadata); $className = $class->name; $class->name = $namespace . '\\' . $className; if ($type === 'annotation') { diff --git a/src/ConnectionFactory.php b/src/ConnectionFactory.php index d762963b..4e9e3bef 100644 --- a/src/ConnectionFactory.php +++ b/src/ConnectionFactory.php @@ -119,7 +119,7 @@ public function createConnection(array $params, ?Configuration $config = null, ? $connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : [])); $params = $this->addDatabaseSuffix(array_merge($connection->getParams(), $overriddenOptions)); $driver = $connection->getDriver(); - /** @psalm-suppress InvalidScalarArgument Bogus error, StaticServerVersionProvider implements Doctrine\DBAL\ServerVersionProvider */ + /** @phpstan-ignore arguments.count (DBAL < 4.x doesn't accept an argument) */ $platform = $driver->getDatabasePlatform( ...(class_exists(StaticServerVersionProvider::class) ? [new StaticServerVersionProvider($params['serverVersion'] ?? $params['primary']['serverVersion'] ?? '')] diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 06cf3dbd..0de8099a 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -277,6 +277,8 @@ private function getDbalConnectionsNode(): ArrayNodeDefinition ->prototype('array'); $this->configureDbalDriverNode($replicaNode); + assert($node instanceof ArrayNodeDefinition); + return $node; } @@ -822,6 +824,8 @@ private function getOrmEntityManagersNode(): ArrayNodeDefinition ->end() ->end(); + assert($node instanceof ArrayNodeDefinition); + return $node; } @@ -850,6 +854,8 @@ private function getOrmCacheDriverNode(string $name): ArrayNodeDefinition $node->addDefaultsIfNotSet(); } + assert($node instanceof ArrayNodeDefinition); + return $node; } diff --git a/src/Registry.php b/src/Registry.php index 958956dc..f3275d5c 100644 --- a/src/Registry.php +++ b/src/Registry.php @@ -6,8 +6,8 @@ use Doctrine\ORM\ORMException; use Doctrine\Persistence\Proxy; use ProxyManager\Proxy\LazyLoadingInterface; -use Psr\Container\ContainerInterface; use Symfony\Bridge\Doctrine\ManagerRegistry; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\VarExporter\LazyObjectInterface; use Symfony\Contracts\Service\ResetInterface; @@ -23,7 +23,7 @@ class Registry extends ManagerRegistry implements ResetInterface * @param string[] $connections * @param string[] $entityManagers */ - public function __construct(ContainerInterface $container, array $connections, array $entityManagers, string $defaultConnection, string $defaultEntityManager) + public function __construct(Container $container, array $connections, array $entityManagers, string $defaultConnection, string $defaultEntityManager) { $this->container = $container; @@ -51,7 +51,7 @@ public function getAliasNamespace($alias) } try { - /** @psalm-suppress UndefinedMethod ORM < 3 specific */ + /** @phpstan-ignore method.notFound (ORM < 3 specific) */ return $objectManager->getConfiguration()->getEntityNamespace($alias); /* @phpstan-ignore class.notFound */ } catch (ORMException $e) { diff --git a/src/Repository/ServiceEntityRepositoryProxy.php b/src/Repository/ServiceEntityRepositoryProxy.php index 07dd6c0a..9755faca 100644 --- a/src/Repository/ServiceEntityRepositoryProxy.php +++ b/src/Repository/ServiceEntityRepositoryProxy.php @@ -27,6 +27,7 @@ */ class ServiceEntityRepositoryProxy extends EntityRepository implements ServiceEntityRepositoryInterface { + /** @var EntityRepository */ private ?EntityRepository $repository = null; /** @param class-string $entityClass The class name of the entity this repository manages */ @@ -111,11 +112,13 @@ protected function getClassMetadata(): ClassMetadata return ($this->repository ??= $this->resolveRepository())->getClassMetadata(); } + /** @phpstan-return AbstractLazyCollection&Selectable */ public function matching(Criteria $criteria): AbstractLazyCollection&Selectable { return ($this->repository ??= $this->resolveRepository())->matching($criteria); } + /** @return EntityRepository */ private function resolveRepository(): EntityRepository { $manager = $this->registry->getManagerForClass($this->entityClass); @@ -127,6 +130,9 @@ private function resolveRepository(): EntityRepository )); } - return new EntityRepository($manager, $manager->getClassMetadata($this->entityClass)); + /** @var ClassMetadata $classMetadata */ + $classMetadata = $manager->getClassMetadata($this->entityClass); + + return new EntityRepository($manager, $classMetadata); } } diff --git a/tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php b/tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php index 9a67e056..0c33a76e 100644 --- a/tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php +++ b/tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php @@ -2,7 +2,7 @@ namespace Fixtures\Bundles\RepositoryServiceBundle\Repository; -use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; /** @@ -11,7 +11,7 @@ */ class TestCustomClassRepoRepository extends EntityRepository { - public function getEntityManager(): EntityManager + public function getEntityManager(): EntityManagerInterface { return parent::getEntityManager(); }