diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 23ded89bf..ffd710fe5 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -14,11 +14,6 @@ jobs: name: "Static Analysis with Psalm" runs-on: "ubuntu-22.04" - strategy: - matrix: - php-version: - - "7.4" - steps: - name: "Checkout code" uses: "actions/checkout@v4" @@ -27,7 +22,7 @@ jobs: uses: "shivammathur/setup-php@v2" with: coverage: "none" - php-version: "${{ matrix.php-version }}" + php-version: "8.2" - name: "Enforce using stable dependencies" run: "composer config minimum-stability stable" diff --git a/ConnectionFactory.php b/ConnectionFactory.php index cd0ed2604..9bd91343d 100644 --- a/ConnectionFactory.php +++ b/ConnectionFactory.php @@ -78,6 +78,7 @@ public function createConnection(array $params, ?Configuration $config = null, ? } $overriddenOptions = []; + /** @psalm-suppress InvalidArrayOffset We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */ if (isset($params['connection_override_options'])) { trigger_deprecation('doctrine/doctrine-bundle', '2.4', 'The "connection_override_options" connection parameter is deprecated'); $overriddenOptions = $params['connection_override_options']; @@ -97,6 +98,7 @@ public function createConnection(array $params, ?Configuration $config = null, ? } } + /** @psalm-suppress InvalidArrayOffset We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */ if (! isset($params['pdo']) && (! isset($params['charset']) || $overriddenOptions || isset($params['dbname_suffix']))) { $wrapperClass = null; diff --git a/DataCollector/DoctrineDataCollector.php b/DataCollector/DoctrineDataCollector.php index 632815e41..f1589d425 100644 --- a/DataCollector/DoctrineDataCollector.php +++ b/DataCollector/DoctrineDataCollector.php @@ -41,7 +41,7 @@ * regions: array<"puts"|"hits"|"misses", array>, * }, * connections: list, - * entities: array>, + * entities: array>, * errors: array>>, * managers: list, * queries: array>, @@ -55,7 +55,7 @@ class DoctrineDataCollector extends BaseCollector /** * @var mixed[][]|null - * @psalm-var ?array> + * @psalm-var ?array> */ private ?array $groupedQueries = null; @@ -184,7 +184,7 @@ public function collect(Request $request, Response $response, ?Throwable $except $this->groupedQueries = null; } - /** @return array> */ + /** @return array> */ public function getEntities() { return $this->data['entities']; @@ -243,7 +243,7 @@ public function getInvalidEntityCount() /** * @return string[][] - * @psalm-return array> + * @psalm-return array> */ public function getGroupedQueries() { diff --git a/DependencyInjection/Compiler/EntityListenerPass.php b/DependencyInjection/Compiler/EntityListenerPass.php index d2e13a526..23692e186 100644 --- a/DependencyInjection/Compiler/EntityListenerPass.php +++ b/DependencyInjection/Compiler/EntityListenerPass.php @@ -94,7 +94,7 @@ public function process(ContainerBuilder $container) } } - /** @param array{entity: class-string, event?: ?string} $attributes */ + /** @param array{entity: class-string, event?: ?string, method?: string} $attributes */ private function attachToListener(ContainerBuilder $container, string $name, string $class, array $attributes): void { $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name); diff --git a/DependencyInjection/DoctrineExtension.php b/DependencyInjection/DoctrineExtension.php index d5d9f3678..cf74faebf 100644 --- a/DependencyInjection/DoctrineExtension.php +++ b/DependencyInjection/DoctrineExtension.php @@ -181,6 +181,7 @@ protected function dbalLoad(array $config, ContainerBuilder $container) $connections = []; foreach (array_keys($config['connections']) as $name) { + /** @psalm-suppress InvalidArrayOffset https://github.com/vimeo/psalm/issues/10382 */ $connections[$name] = sprintf('doctrine.dbal.%s_connection', $name); } @@ -533,6 +534,7 @@ protected function ormLoad(array $config, ContainerBuilder $container) $entityManagers = []; foreach (array_keys($config['entity_managers']) as $name) { + /** @psalm-suppress InvalidArrayOffset */ $entityManagers[$name] = sprintf('doctrine.orm.%s_entity_manager', $name); } @@ -547,7 +549,6 @@ protected function ormLoad(array $config, ContainerBuilder $container) if ($config['enable_lazy_ghost_objects'] ?? false) { // available in Symfony 6.2 and higher - /** @psalm-suppress UndefinedClass */ if (! trait_exists(LazyGhostTrait::class)) { throw new LogicException( 'Lazy ghost objects cannot be enabled because the "symfony/var-exporter" library' @@ -838,10 +839,12 @@ protected function loadOrmEntityManagerMappingInformation(array $entityManager, $this->registerMappingDrivers($entityManager, $container); $container->getDefinition($this->getObjectManagerElementName($entityManager['name'] . '_metadata_driver')); + /** @psalm-suppress NoValue $this->drivers is set by $this->loadMappingInformation() call */ foreach (array_keys($this->drivers) as $driverType) { $mappingService = $this->getObjectManagerElementName($entityManager['name'] . '_' . $driverType . '_metadata_driver'); $mappingDriverDef = $container->getDefinition($mappingService); $args = $mappingDriverDef->getArguments(); + /** @psalm-suppress TypeDoesNotContainType $this->drivers is set by $this->loadMappingInformation() call */ if ($driverType === 'annotation') { $args[2] = $entityManager['report_fields_where_declared']; } elseif ($driverType === 'attribute') { diff --git a/DoctrineBundle.php b/DoctrineBundle.php index ebf2a7640..6725c6184 100644 --- a/DoctrineBundle.php +++ b/DoctrineBundle.php @@ -97,6 +97,7 @@ public function boot() if ($this->container->getParameter('doctrine.orm.auto_generate_proxy_classes')) { // See https://github.com/symfony/symfony/pull/3419 for usage of references + /** @psalm-suppress UnsupportedPropertyReferenceUsage */ $container = &$this->container; $proxyGenerator = static function ($proxyDir, $proxyNamespace, $class) use (&$container): void { diff --git a/Mapping/DisconnectedMetadataFactory.php b/Mapping/DisconnectedMetadataFactory.php index e5fcc73af..a9604821a 100644 --- a/Mapping/DisconnectedMetadataFactory.php +++ b/Mapping/DisconnectedMetadataFactory.php @@ -117,10 +117,12 @@ public function findNamespaceAndPathForMetadata(ClassMetadataCollection $metadat $ns = $r->getNamespaceName(); } elseif ($path) { // Get namespace by removing the last component of the FQCN + /** @psalm-suppress NoValue */ $nsParts = explode('\\', $all[0]->name); array_pop($nsParts); $ns = implode('\\', $nsParts); } else { + /** @psalm-suppress NoValue */ throw new RuntimeException(sprintf('Unable to determine where to save the "%s" class (use the --path option).', $all[0]->name)); } diff --git a/Repository/ServiceEntityRepositoryProxy.php b/Repository/ServiceEntityRepositoryProxy.php index 9ff9898fe..6eca5dc84 100644 --- a/Repository/ServiceEntityRepositoryProxy.php +++ b/Repository/ServiceEntityRepositoryProxy.php @@ -41,25 +41,35 @@ public function __construct( $this->repository = $this->resolveRepository(); } + /** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */ public function createQueryBuilder(string $alias, ?string $indexBy = null): QueryBuilder { return ($this->repository ??= $this->resolveRepository()) ->createQueryBuilder($alias, $indexBy); } + /** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */ public function createResultSetMappingBuilder(string $alias): ResultSetMappingBuilder { return ($this->repository ??= $this->resolveRepository()) ->createResultSetMappingBuilder($alias); } + /** @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class */ public function find(mixed $id, LockMode|int|null $lockMode = null, int|null $lockVersion = null): object|null { + /** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */ return ($this->repository ??= $this->resolveRepository()) ->find($id, $lockMode, $lockVersion); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class + * @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class + * @psalm-suppress InvalidReturnType This proxy is used only in combination with newer parent class + */ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array { return ($this->repository ??= $this->resolveRepository()) @@ -69,6 +79,7 @@ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = nu /** {@inheritDoc} */ public function findOneBy(array $criteria, ?array $orderBy = null): object|null { + /** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */ return ($this->repository ??= $this->resolveRepository()) ->findOneBy($criteria, $orderBy); } @@ -79,7 +90,11 @@ public function count(array $criteria = []): int return ($this->repository ??= $this->resolveRepository())->count($criteria); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @psalm-suppress MethodSignatureMismatch This proxy is used only in combination with newer parent class + */ public function __call(string $method, array $arguments): mixed { return ($this->repository ??= $this->resolveRepository())->$method(...$arguments); @@ -95,8 +110,10 @@ protected function getEntityManager(): EntityManagerInterface return ($this->repository ??= $this->resolveRepository())->getEntityManager(); } + /** @psalm-suppress InvalidReturnType This proxy is used only in combination with newer parent class */ protected function getClassMetadata(): ClassMetadata { + /** @psalm-suppress InvalidReturnStatement This proxy is used only in combination with newer parent class */ return ($this->repository ??= $this->resolveRepository())->getClassMetadata(); } diff --git a/Tests/CacheSchemaSubscriberTest.php b/Tests/CacheSchemaSubscriberTest.php index 79723af60..defc2b8f0 100644 --- a/Tests/CacheSchemaSubscriberTest.php +++ b/Tests/CacheSchemaSubscriberTest.php @@ -95,13 +95,13 @@ public function getSchemaSubscribers(): Generator { /** * available in Symfony 6.3 - * - * @psalm-suppress UndefinedClass */ yield ['cache.adapter.doctrine_dbal', 'doctrine.orm.listeners.doctrine_dbal_cache_adapter_schema_listener', DoctrineDbalCacheAdapterSchemaListener::class]; /** * available in Symfony 5.1 and up to Symfony 5.4 (deprecated) + * + * @psalm-suppress UndefinedClass */ yield ['cache.adapter.pdo', 'doctrine.orm.listeners.pdo_cache_adapter_doctrine_schema_subscriber', PdoCacheAdapterDoctrineSchemaSubscriber::class]; } diff --git a/Tests/Command/CreateDatabaseDoctrineTest.php b/Tests/Command/CreateDatabaseDoctrineTest.php index 4fc8b735a..da3c46bea 100644 --- a/Tests/Command/CreateDatabaseDoctrineTest.php +++ b/Tests/Command/CreateDatabaseDoctrineTest.php @@ -86,7 +86,7 @@ private function getMockContainer(string $connectionName, ?array $params = null) ->willReturn($mockConnection); $mockContainer = $this->getMockBuilder(Container::class) - ->setMethods(['get']) + ->onlyMethods(['get']) ->getMock(); $mockContainer->expects($this->any()) diff --git a/Tests/Command/DropDatabaseDoctrineTest.php b/Tests/Command/DropDatabaseDoctrineTest.php index adc5fc82c..a39e4fbc9 100644 --- a/Tests/Command/DropDatabaseDoctrineTest.php +++ b/Tests/Command/DropDatabaseDoctrineTest.php @@ -168,7 +168,7 @@ private function getMockContainer(string $connectionName, array $params): MockOb ->willReturn($mockConnection); $mockContainer = $this->getMockBuilder(Container::class) - ->setMethods(['get']) + ->onlyMethods(['get']) ->getMock(); $mockContainer->expects($this->any()) diff --git a/Tests/ConnectionFactoryTest.php b/Tests/ConnectionFactoryTest.php index 60726c48a..ced8945a2 100644 --- a/Tests/ConnectionFactoryTest.php +++ b/Tests/ConnectionFactoryTest.php @@ -96,6 +96,7 @@ public function testConnectionOverrideOptions(): void 'password' => 'wordpass', ]; + /** @psalm-suppress InvalidArgument We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */ $connection = (new ConnectionFactory([]))->createConnection( [ 'url' => 'mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8', @@ -119,6 +120,7 @@ public function testConnectionCharsetFromUrl() public function testDbnameSuffix(): void { + /** @psalm-suppress InvalidArgument We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */ $connection = (new ConnectionFactory([]))->createConnection( [ 'url' => 'mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8', @@ -132,6 +134,7 @@ public function testDbnameSuffix(): void public function testDbnameSuffixForReplicas(): void { + /** @psalm-suppress InvalidArgument We should adjust when https://github.com/vimeo/psalm/issues/8984 is fixed */ $connection = (new ConnectionFactory([]))->createConnection( [ 'driver' => 'pdo_mysql', diff --git a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php index fcb21d96b..daa094d4b 100644 --- a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php +++ b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -8,11 +8,11 @@ use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; +use Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener; use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; -use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; use Doctrine\ORM\Configuration as OrmConfiguration; use Doctrine\ORM\EntityManager; @@ -58,7 +58,6 @@ use const DIRECTORY_SEPARATOR; -/** @psalm-import-type Params from DriverManager */ abstract class AbstractDoctrineExtensionTest extends TestCase { abstract protected function loadFromFile(ContainerBuilder $container, string $file): void; @@ -366,10 +365,7 @@ public function testLoadSimpleSingleConnectionWithoutDbName(): void $container = $this->loadContainer('orm_service_simple_single_entity_manager_without_dbname'); - $definition = $container->getDefinition('doctrine.dbal.default_connection'); - assert($definition instanceof Definition); - - $this->assertDICConstructorArguments($definition, [ + $this->assertDICConstructorArguments($container->getDefinition('doctrine.dbal.default_connection'), [ [ 'host' => 'localhost', 'port' => null, @@ -946,8 +942,8 @@ public function testAddFilter(): void $definition = $container->getDefinition('doctrine.orm.default_configuration'); $args = [ - ['soft_delete', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\TestFilter'], - ['myFilter', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\TestFilter'], + ['soft_delete', TestFilter::class], + ['myFilter', TestFilter::class], ]; $this->assertDICDefinitionMethodCallCount($definition, 'addFilter', $args, 2); @@ -1229,16 +1225,16 @@ public function testEntityListenerResolver(): void $container = $this->loadContainer('orm_entity_listener_resolver', ['YamlBundle'], new EntityListenerPass()); $definition = $container->getDefinition('doctrine.orm.em1_configuration'); - $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityListenerResolver', [new Reference('doctrine.orm.em1_entity_listener_resolver')]); + $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityListenerResolver', ['doctrine.orm.em1_entity_listener_resolver']); $definition = $container->getDefinition('doctrine.orm.em2_configuration'); - $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityListenerResolver', [new Reference('doctrine.orm.em2_entity_listener_resolver')]); + $this->assertDICDefinitionMethodCallOnce($definition, 'setEntityListenerResolver', ['doctrine.orm.em2_entity_listener_resolver']); $listener = $container->getDefinition('doctrine.orm.em1_entity_listener_resolver'); $this->assertDICDefinitionMethodCallOnce($listener, 'registerService', ['EntityListener', 'entity_listener1']); $listener = $container->getDefinition('entity_listener_resolver'); - $this->assertDICDefinitionMethodCallOnce($listener, 'register', [new Reference('entity_listener2')]); + $this->assertDICDefinitionMethodCallOnce($listener, 'register', ['entity_listener2']); } public function testAttachEntityListenerTag(): void @@ -1260,8 +1256,8 @@ public function testAttachEntityListenerTag(): void $this->assertDICDefinitionMethodCallCount($listener, 'registerService', [ ['ParentEntityListener', 'children_entity_listener'], ['EntityListener1', 'entity_listener1'], - ['Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'invokable_entity_listener'], - ['Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'invokable_entity_listener'], + [InvokableEntityListener::class, 'invokable_entity_listener'], + [InvokableEntityListener::class, 'invokable_entity_listener'], ], 4); $listener = $container->getDefinition('doctrine.orm.em2_entity_listener_resolver'); @@ -1269,8 +1265,8 @@ public function testAttachEntityListenerTag(): void $attachListener = $container->getDefinition('doctrine.orm.em1_listeners.attach_entity_listeners'); $this->assertDICDefinitionMethodCallAt(1, $attachListener, 'addEntityListener', ['My/Entity1', 'EntityListener1', 'postLoad']); - $this->assertDICDefinitionMethodCallAt(2, $attachListener, 'addEntityListener', ['My/Entity1', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'loadClassMetadata', '__invoke']); - $this->assertDICDefinitionMethodCallAt(3, $attachListener, 'addEntityListener', ['My/Entity1', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'postPersist']); + $this->assertDICDefinitionMethodCallAt(2, $attachListener, 'addEntityListener', ['My/Entity1', InvokableEntityListener::class, 'loadClassMetadata', '__invoke']); + $this->assertDICDefinitionMethodCallAt(3, $attachListener, 'addEntityListener', ['My/Entity1', InvokableEntityListener::class, 'postPersist']); $this->assertDICDefinitionMethodCallAt(0, $attachListener, 'addEntityListener', ['My/Entity3', 'ParentEntityListener', 'postLoad']); $attachListener = $container->getDefinition('doctrine.orm.em2_listeners.attach_entity_listeners'); @@ -1293,24 +1289,15 @@ public function testAttachEntityListenersTwoConnections(): void $this->compileContainer($container); $defaultEventManager = $container->getDefinition('doctrine.dbal.default_connection.event_manager'); - $this->assertDICDefinitionNoMethodCall($defaultEventManager, 'addEventListener', [['loadClassMetadata'], new Reference('doctrine.orm.em2_listeners.attach_entity_listeners')]); + $this->assertEmpty($defaultEventManager->getMethodCalls()); $defaultEventManagerArguments = $defaultEventManager->getArguments(); - if (isset($defaultEventManagerArguments[1][1])) { - $this->assertSame([['loadClassMetadata'], 'doctrine.orm.em1_listeners.attach_entity_listeners'], end($defaultEventManagerArguments[1])); - } else { - $this->assertDICDefinitionMethodCallOnce($defaultEventManager, 'addEventListener', [['loadClassMetadata'], new Reference('doctrine.orm.em1_listeners.attach_entity_listeners')]); - } + $this->assertSame([['loadClassMetadata'], 'doctrine.orm.em1_listeners.attach_entity_listeners'], end($defaultEventManagerArguments[1])); $foobarEventManager = $container->getDefinition('doctrine.dbal.foobar_connection.event_manager'); - $this->assertDICDefinitionNoMethodCall($foobarEventManager, 'addEventListener', [['loadClassMetadata'], new Reference('doctrine.orm.em1_listeners.attach_entity_listeners')]); + $this->assertEmpty($foobarEventManager->getMethodCalls()); $foobarEventManagerArguments = $foobarEventManager->getArguments(); - - if (isset($foobarEventManagerArguments[1][1])) { - $this->assertSame([['loadClassMetadata'], 'doctrine.orm.em2_listeners.attach_entity_listeners'], end($foobarEventManagerArguments[1])); - } else { - $this->assertDICDefinitionMethodCallOnce($foobarEventManager, 'addEventListener', [['loadClassMetadata'], new Reference('doctrine.orm.em2_listeners.attach_entity_listeners')]); - } + $this->assertSame([['loadClassMetadata'], 'doctrine.orm.em2_listeners.attach_entity_listeners'], end($foobarEventManagerArguments[1])); } public function testAttachLazyEntityListener(): void @@ -1330,7 +1317,7 @@ public function testAttachLazyEntityListener(): void $resolver1 = $container->getDefinition('doctrine.orm.em1_entity_listener_resolver'); $this->assertDICDefinitionMethodCallAt(0, $resolver1, 'registerService', ['EntityListener1', 'entity_listener1']); - $this->assertDICDefinitionMethodCallAt(1, $resolver1, 'register', [new Reference('entity_listener3')]); + $this->assertDICDefinitionMethodCallAt(1, $resolver1, 'register', ['entity_listener3']); $this->assertDICDefinitionMethodCallAt(2, $resolver1, 'registerService', ['EntityListener4', 'entity_listener4']); $serviceLocatorReference = $resolver1->getArgument(0); @@ -1523,10 +1510,7 @@ private function assertDICConstructorArguments(Definition $definition, array $ar $this->assertEquals($args, $definition->getArguments(), "Expected and actual DIC Service constructor arguments of definition '" . $definition->getClass() . "' don't match."); } - /** - * @param list $params - * @psalm-param Params $params - */ + /** @param list $params */ private function assertDICDefinitionMethodCallAt( int $pos, Definition $definition, @@ -1551,7 +1535,6 @@ private function assertDICDefinitionMethodCallAt( * Assertion for the DI Container, check if the given definition contains a method call with the given parameters. * * @param list $params - * @psalm-param Params $params */ private function assertDICDefinitionMethodCallOnce( Definition $definition, @@ -1582,10 +1565,7 @@ private function assertDICDefinitionMethodCallOnce( $this->fail("Method '" . $methodName . "' is expected to be called once, definition does not contain a call though."); } - /** - * @param list $params - * @psalm-param Params $params - */ + /** @param list> $params */ private function assertDICDefinitionMethodCallCount( Definition $definition, string $methodName, @@ -1613,31 +1593,6 @@ private function assertDICDefinitionMethodCallCount( $this->assertEquals($nbCalls, $called, sprintf('The method "%s" should be called %d times', $methodName, $nbCalls)); } - /** - * Assertion for the DI Container, check if the given definition does not contain a method call with the given parameters. - * - * @param list $params - * @psalm-param Params $params - */ - private function assertDICDefinitionNoMethodCall( - Definition $definition, - string $methodName, - ?array $params = null - ): void { - $calls = $definition->getMethodCalls(); - foreach ($calls as $call) { - if ($call[0] !== $methodName) { - continue; - } - - if ($params !== null) { - $this->assertNotEquals($params, $call[1], "Method '" . $methodName . "' is not expected to be called with the given parameters."); - } else { - $this->fail("Method '" . $methodName . "' is not expected to be called"); - } - } - } - private function compileContainer(ContainerBuilder $container): void { $passConfig = $container->getCompilerPassConfig(); diff --git a/Tests/DependencyInjection/Compiler/MiddlewarePassTest.php b/Tests/DependencyInjection/Compiler/MiddlewarePassTest.php index ca6151548..06dc92cb5 100644 --- a/Tests/DependencyInjection/Compiler/MiddlewarePassTest.php +++ b/Tests/DependencyInjection/Compiler/MiddlewarePassTest.php @@ -135,7 +135,7 @@ public function testAddMiddlewareWithAttributeForAutoconfiguration(string $class } /** @dataProvider provideAddMiddleware */ - public function testDontAddMiddlewareWhenDbalIsNotUsed(string $middlewareClass, bool $connectionNameAware): void + public function testDontAddMiddlewareWhenDbalIsNotUsed(string $middlewareClass): void { $container = $this->createContainer(static function (ContainerBuilder $container) use ($middlewareClass) { $container diff --git a/Tests/DependencyInjection/DoctrineExtensionTest.php b/Tests/DependencyInjection/DoctrineExtensionTest.php index ffa3b987b..ce6cfbc41 100644 --- a/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -308,7 +308,7 @@ public function getAutomappingConfigurations(): array * * @dataProvider getAutomappingConfigurations */ - public function testAutomapping(array $entityManagers): void + public static function testAutomapping(array $entityManagers): void { if (! interface_exists(EntityManagerInterface::class)) { self::markTestSkipped('This test requires ORM'); @@ -316,7 +316,7 @@ public function testAutomapping(array $entityManagers): void $extension = new DoctrineExtension(); - $container = $this->getContainer([ + $container = self::getContainer([ 'YamlBundle', 'XmlBundle', 'NewXmlBundle', @@ -342,7 +342,7 @@ public function testAutomapping(array $entityManagers): void $configEm2 = $container->getDefinition('doctrine.orm.em2_configuration'); $configEm3 = $container->getDefinition('doctrine.orm.em3_configuration'); - $this->assertContains( + self::assertContains( [ 'setEntityNamespaces', [ @@ -352,7 +352,7 @@ public function testAutomapping(array $entityManagers): void $configEm1->getMethodCalls(), ); - $this->assertContains( + self::assertContains( [ 'setEntityNamespaces', [ @@ -362,7 +362,7 @@ public function testAutomapping(array $entityManagers): void $configEm2->getMethodCalls(), ); - $this->assertContains( + self::assertContains( [ 'setEntityNamespaces', [ @@ -1001,16 +1001,12 @@ public function testMessengerIntegration(): void ->build(); $extension->load([$config], $container); - $this->assertNotNull($middlewarePrototype = $container->getDefinition('messenger.middleware.doctrine_transaction')); - $this->assertCount(1, $middlewarePrototype->getArguments()); - $this->assertNotNull($middlewarePrototype = $container->getDefinition('messenger.middleware.doctrine_ping_connection')); - $this->assertCount(1, $middlewarePrototype->getArguments()); - $this->assertNotNull($middlewarePrototype = $container->getDefinition('messenger.middleware.doctrine_close_connection')); - $this->assertCount(1, $middlewarePrototype->getArguments()); + $this->assertCount(1, $container->getDefinition('messenger.middleware.doctrine_transaction')->getArguments()); + $this->assertCount(1, $container->getDefinition('messenger.middleware.doctrine_ping_connection')->getArguments()); + $this->assertCount(1, $container->getDefinition('messenger.middleware.doctrine_close_connection')->getArguments()); if (class_exists(DoctrineClearEntityManagerWorkerSubscriber::class)) { - $this->assertNotNull($subscriber = $container->getDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager')); - $this->assertCount(1, $subscriber->getArguments()); + $this->assertCount(1, $container->getDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager')->getArguments()); } else { $this->assertFalse($container->hasDefinition('doctrine.orm.messenger.event_subscriber.doctrine_clear_entity_manager')); } @@ -1095,11 +1091,11 @@ public function testInvalidCacheConfiguration(): void } /** - * @param array{pool?: string, type: ?string} $cacheConfig + * @param array{pool?: string, type: ?string, id?: string} $cacheConfig * * @dataProvider cacheConfigurationProvider */ - public function testCacheConfiguration(string $expectedAliasName, string $expectedTarget, string $cacheName, $cacheConfig): void + public function testCacheConfiguration(string $expectedAliasName, string $expectedTarget, string $cacheName, array $cacheConfig): void { if (! interface_exists(EntityManagerInterface::class)) { self::markTestSkipped('This test requires ORM'); @@ -1121,7 +1117,7 @@ public function testCacheConfiguration(string $expectedAliasName, string $expect } /** - * @param array{pool?: string, type: ?string} $cacheConfig + * @param array{type: ?string, pool?: string, id?: string} $cacheConfig * * @dataProvider legacyCacheConfigurationProvider * @group legacy @@ -1131,7 +1127,7 @@ public function testLegacyCacheConfiguration(string $expectedAliasName, string $ $this->testCacheConfiguration($expectedAliasName, $expectedAliasTarget, $cacheName, $cacheConfig); } - /** @return array> */ + /** @return array */ public static function legacyCacheConfigurationProvider(): array { return [ @@ -1168,7 +1164,7 @@ public static function legacyCacheConfigurationProvider(): array ]; } - /** @return array> */ + /** @return array> */ public static function cacheConfigurationProvider(): array { return [ @@ -1233,8 +1229,7 @@ public function testAsEntityListenerAttribute() $reflector = new ReflectionClass(Php8EntityListener::class); $definition = new ChildDefinition(''); - /** @psalm-suppress UndefinedMethod */ - $attribute = $reflector->getAttributes(AsEntityListener::class)[0]->newInstance(); + $attribute = $reflector->getAttributes(AsEntityListener::class)[0]->newInstance(); $attributes[AsEntityListener::class]($definition, $attribute); @@ -1271,8 +1266,7 @@ public function testAsDoctrineListenerAttribute() $reflector = new ReflectionClass(Php8EventListener::class); $definition = new ChildDefinition(''); - /** @psalm-suppress UndefinedMethod */ - $attribute = $reflector->getAttributes(AsDoctrineListener::class)[0]->newInstance(); + $attribute = $reflector->getAttributes(AsDoctrineListener::class)[0]->newInstance(); $attributes[AsDoctrineListener::class]($definition, $attribute); @@ -1471,7 +1465,7 @@ public function testControllerResolver(bool $simpleEntityManagerConfig): void // phpcs:enable /** @param list $bundles */ - private function getContainer(array $bundles = ['YamlBundle'], string $vendor = ''): ContainerBuilder + private static function getContainer(array $bundles = ['YamlBundle'], string $vendor = ''): ContainerBuilder { $map = []; $metadataMap = []; diff --git a/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php b/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php index 23994e24f..9a67e056b 100644 --- a/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php +++ b/Tests/DependencyInjection/Fixtures/Bundles/RepositoryServiceBundle/Repository/TestCustomClassRepoRepository.php @@ -5,6 +5,10 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; +/** + * @template T of object + * @extends EntityRepository + */ class TestCustomClassRepoRepository extends EntityRepository { public function getEntityManager(): EntityManager diff --git a/Tests/DependencyInjection/Fixtures/DbalTestKernel.php b/Tests/DependencyInjection/Fixtures/DbalTestKernel.php index e40f0e01a..32d17a26b 100644 --- a/Tests/DependencyInjection/Fixtures/DbalTestKernel.php +++ b/Tests/DependencyInjection/Fixtures/DbalTestKernel.php @@ -68,9 +68,4 @@ public function getProjectDir(): string { return $this->projectDir ??= sys_get_temp_dir() . '/sf_kernel_' . md5((string) mt_rand()); } - - public function getRootDir(): string - { - return $this->getProjectDir(); - } } diff --git a/Tests/DependencyInjection/Fixtures/TestKernel.php b/Tests/DependencyInjection/Fixtures/TestKernel.php index 7bcf1999c..4ad688190 100644 --- a/Tests/DependencyInjection/Fixtures/TestKernel.php +++ b/Tests/DependencyInjection/Fixtures/TestKernel.php @@ -73,9 +73,4 @@ public function getProjectDir(): string { return $this->projectDir ??= sys_get_temp_dir() . '/sf_kernel_' . md5((string) mt_rand()); } - - public function getRootDir(): string - { - return $this->getProjectDir(); - } } diff --git a/Tests/LazyLoadingEntityManagerInterface.php b/Tests/LazyLoadingEntityManagerInterface.php index f1b157e67..70da6c467 100644 --- a/Tests/LazyLoadingEntityManagerInterface.php +++ b/Tests/LazyLoadingEntityManagerInterface.php @@ -5,6 +5,10 @@ use Doctrine\ORM\EntityManagerInterface; use ProxyManager\Proxy\LazyLoadingInterface; +/** + * @template LazilyLoadedObjectType of object + * @extends LazyLoadingInterface + */ interface LazyLoadingEntityManagerInterface extends LazyLoadingInterface, EntityManagerInterface { } diff --git a/Tests/Mapping/ContainerEntityListenerResolverTest.php b/Tests/Mapping/ContainerEntityListenerResolverTest.php index 0f2e58bd6..a4bde56f9 100644 --- a/Tests/Mapping/ContainerEntityListenerResolverTest.php +++ b/Tests/Mapping/ContainerEntityListenerResolverTest.php @@ -80,8 +80,8 @@ public function testRegisterMissingServiceAndResolve(): void public function testClearOne(): void { - $className1 = '\Doctrine\Bundle\DoctrineBundle\Tests\Mapping\EntityListener1'; - $className2 = '\Doctrine\Bundle\DoctrineBundle\Tests\Mapping\EntityListener2'; + $className1 = EntityListener1::class; + $className2 = EntityListener2::class; $obj1 = $this->resolver->resolve($className1); $obj2 = $this->resolver->resolve($className2); diff --git a/Tests/RegistryTest.php b/Tests/RegistryTest.php index b6246f336..9c4ff08e3 100644 --- a/Tests/RegistryTest.php +++ b/Tests/RegistryTest.php @@ -155,9 +155,7 @@ public function testResetLazyObject(): void self::markTestSkipped('This test requires ORM and VarExporter 6.2+'); } - /** @psalm-suppress MissingDependency https://github.com/vimeo/psalm/issues/8258 */ $ghostManager = $this->createMock(LazyObjectEntityManagerInterface::class); - /** @psalm-suppress MissingDependency https://github.com/vimeo/psalm/issues/8258 */ $ghostManager->expects($this->once())->method('resetLazyObject')->willReturn(true); $container = new Container(); diff --git a/Tests/Repository/Fixtures/StubRepository.php b/Tests/Repository/Fixtures/StubRepository.php index d68e4aea2..a68532dcf 100644 --- a/Tests/Repository/Fixtures/StubRepository.php +++ b/Tests/Repository/Fixtures/StubRepository.php @@ -6,6 +6,10 @@ use Doctrine\ORM\EntityRepository; +/** + * @template T of object + * @extends EntityRepository + */ class StubRepository extends EntityRepository { } diff --git a/Tests/Repository/Fixtures/StubServiceRepository.php b/Tests/Repository/Fixtures/StubServiceRepository.php index 16b52d983..31b548eea 100644 --- a/Tests/Repository/Fixtures/StubServiceRepository.php +++ b/Tests/Repository/Fixtures/StubServiceRepository.php @@ -7,6 +7,10 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface; use Doctrine\ORM\EntityRepository; +/** + * @template T of object + * @extends EntityRepository + */ class StubServiceRepository extends EntityRepository implements ServiceEntityRepositoryInterface { } diff --git a/Tests/Repository/ServiceEntityRepositoryTest.php b/Tests/Repository/ServiceEntityRepositoryTest.php index 97fb54a4d..cffe0c32d 100644 --- a/Tests/Repository/ServiceEntityRepositoryTest.php +++ b/Tests/Repository/ServiceEntityRepositoryTest.php @@ -41,10 +41,7 @@ public function testConstructInitializesWhenImplementingLazyObjectInterface(): v $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); $this->expectException(LogicException::class); - /** - * @psalm-suppress MissingDependency - * @psalm-suppress UndefinedClass - */ + /** @psalm-suppress UndefinedClass */ new class ($registry, TestEntity::class) extends ServiceEntityRepository implements LazyObjectInterface { use LazyGhostTrait; }; diff --git a/Tests/UrlOverrideTest.php b/Tests/UrlOverrideTest.php index 83c9be2c7..bd5e5682e 100644 --- a/Tests/UrlOverrideTest.php +++ b/Tests/UrlOverrideTest.php @@ -15,12 +15,12 @@ class UrlOverrideTest extends TestCase * * @dataProvider connectionDataProvider */ - public function testConnectionConfiguration(array $config, array $expectedParams): void + public static function testConnectionConfiguration(array $config, array $expectedParams): void { $kernel = new DbalTestKernel($config); $kernel->boot(); - $this->assertEquals( + self::assertEquals( $expectedParams, array_intersect_key( $kernel->getContainer()->get('doctrine.dbal.default_connection')->getParams(), @@ -29,7 +29,7 @@ public function testConnectionConfiguration(array $config, array $expectedParams ); } - /** @return array>> */ + /** @return array, 1: array}> */ public function connectionDataProvider(): array { return [ diff --git a/Twig/DoctrineExtension.php b/Twig/DoctrineExtension.php index 063681a44..7c5f75394 100644 --- a/Twig/DoctrineExtension.php +++ b/Twig/DoctrineExtension.php @@ -27,6 +27,8 @@ /** * This class contains the needed functions in order to do the query highlighting + * + * @internal since 2.11 */ class DoctrineExtension extends AbstractExtension { @@ -192,14 +194,4 @@ private function setUpSqlFormatter(bool $highlight = true, bool $legacy = false) HtmlHighlighter::HIGHLIGHT_VARIABLE => 'class="variable"', ], ! $legacy) : new NullHighlighter()); } - - /** - * Get the name of the extension - * - * @return string - */ - public function getName() - { - return 'doctrine_extension'; - } } diff --git a/composer.json b/composer.json index 89a39daad..6af31dc4d 100644 --- a/composer.json +++ b/composer.json @@ -50,9 +50,9 @@ "doctrine/deprecations": "^1.0", "doctrine/orm": "^2.14 || ^3.0", "friendsofphp/proxy-manager-lts": "^1.0", - "phpunit/phpunit": "^9.5.26 || ^10.0", + "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.4", - "psalm/plugin-symfony": "^4", + "psalm/plugin-symfony": "^5", "psr/log": "^1.1.4 || ^2.0 || ^3.0", "symfony/phpunit-bridge": "^6.1 || ^7.0", "symfony/property-info": "^5.4 || ^6.0 || ^7.0", @@ -65,7 +65,7 @@ "symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0", "twig/twig": "^1.34 || ^2.12 || ^3.0", - "vimeo/psalm": "^4.30" + "vimeo/psalm": "^5.15" }, "conflict": { "doctrine/annotations": ">=3.0", diff --git a/psalm.xml.dist b/psalm.xml.dist index 107b4708e..2d06cbbb4 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -1,8 +1,11 @@ @@ -16,8 +19,6 @@ - - @@ -36,12 +37,6 @@ - - - - - - @@ -52,25 +47,8 @@ - - - - - - - - - - - - - - - - - @@ -78,5 +56,11 @@ + + + + + +