From ad3850aec1afae14c2a462988f64edf2289c3a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 15:03:45 +0200 Subject: [PATCH 1/7] Add integration test for entity manager dynamic return --- phpstan.neon | 4 ++ .../ORM/EntityManagerIntegrationTest.php | 33 ++++++++++ .../ORM/data/entityManagerDynamicReturn.php | 62 +++++++++++++++++++ tests/DoctrineIntegration/ORM/phpstan.neon | 2 + 4 files changed, 101 insertions(+) create mode 100644 tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php create mode 100644 tests/DoctrineIntegration/ORM/data/entityManagerDynamicReturn.php create mode 100644 tests/DoctrineIntegration/ORM/phpstan.neon diff --git a/phpstan.neon b/phpstan.neon index 78c68c2d..d72de844 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,3 +2,7 @@ includes: - vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon + +parameters: + excludes_analyse: + - */tests/*/data/* diff --git a/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php new file mode 100644 index 00000000..ec75b48f --- /dev/null +++ b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php @@ -0,0 +1,33 @@ +entityManager = $entityManager; + } + + public function findDynamicType(): void + { + $test = $this->entityManager->find(MyEntity::class, 1); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function getReferenceDynamicType(): void + { + $test = $this->entityManager->getReference(MyEntity::class, 1); + $test->doSomething(); + } + + public function getPartialReferenceDynamicType(): void + { + $test = $this->entityManager->getPartialReference(MyEntity::class, 1); + $test->doSomething(); + } +} + +/** + * @ORM\Entity() + */ +class MyEntity +{ + /** + * @ORM\Id() + * @ORM\GeneratedValue() + * @ORM\Column(type="integer") + * + * @var int + */ + private $id; + + public function doSomething(): void + { + } +} diff --git a/tests/DoctrineIntegration/ORM/phpstan.neon b/tests/DoctrineIntegration/ORM/phpstan.neon new file mode 100644 index 00000000..e6dd2808 --- /dev/null +++ b/tests/DoctrineIntegration/ORM/phpstan.neon @@ -0,0 +1,2 @@ +includes: + - ../../../extension.neon From 68786efc0986e234875702a1aae79e8633f97aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 15:04:40 +0200 Subject: [PATCH 2/7] Fix return type of entity manager dynamic return The methods `EntityManagerInterface#getReference()` and `EntityManagerInterface#getPartialReference()` have nullable return. That's properly documented for `getReference()` but had to be fixed for `getPartialReference()`. --- .../EntityManagerFindDynamicReturnTypeExtension.php | 7 +------ .../ORM/data/entityManagerDynamicReturn.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php b/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php index 2498cc8d..fa0884d6 100644 --- a/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php @@ -43,12 +43,7 @@ public function getTypeFromMethodCall( return $mixedType; } - $type = new ObjectType($argType->getValue()); - if ($methodReflection->getName() === 'find') { - $type = TypeCombinator::addNull($type); - } - - return $type; + return TypeCombinator::addNull(new ObjectType($argType->getValue())); } } diff --git a/tests/DoctrineIntegration/ORM/data/entityManagerDynamicReturn.php b/tests/DoctrineIntegration/ORM/data/entityManagerDynamicReturn.php index 71b2cade..0f18ec85 100644 --- a/tests/DoctrineIntegration/ORM/data/entityManagerDynamicReturn.php +++ b/tests/DoctrineIntegration/ORM/data/entityManagerDynamicReturn.php @@ -32,12 +32,22 @@ public function findDynamicType(): void public function getReferenceDynamicType(): void { $test = $this->entityManager->getReference(MyEntity::class, 1); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + $test->doSomething(); } public function getPartialReferenceDynamicType(): void { $test = $this->entityManager->getPartialReference(MyEntity::class, 1); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + $test->doSomething(); } } From b2fb483b41e70e7e60c5984944bd2d7bbcdaa974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 15:22:13 +0200 Subject: [PATCH 3/7] Add integration test for entity repository dynamic return --- .../ORM/EntityManagerIntegrationTest.php | 5 +- .../data/entityRepositoryDynamicReturn.php | 80 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/DoctrineIntegration/ORM/data/entityRepositoryDynamicReturn.php diff --git a/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php index ec75b48f..b41bf007 100644 --- a/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php +++ b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php @@ -12,7 +12,10 @@ final class EntityManagerIntegrationTest extends LevelsTestCase */ public function dataTopics(): array { - return [['entityManagerDynamicReturn']]; + return [ + ['entityManagerDynamicReturn'], + ['entityRepositoryDynamicReturn'], + ]; } public function getDataPath(): string diff --git a/tests/DoctrineIntegration/ORM/data/entityRepositoryDynamicReturn.php b/tests/DoctrineIntegration/ORM/data/entityRepositoryDynamicReturn.php new file mode 100644 index 00000000..d26b037d --- /dev/null +++ b/tests/DoctrineIntegration/ORM/data/entityRepositoryDynamicReturn.php @@ -0,0 +1,80 @@ +repository = $entityManager->getRepository(MyEntity::class); + } + + public function findDynamicType(): void + { + $test = $this->repository->find(1); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function findOneByDynamicType(): void + { + $test = $this->repository->findOneBy(['blah' => 'testing']); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function findAllDynamicType(): void + { + $items = $this->repository->findAll(); + + foreach ($items as $test) { + $test->doSomething(); + } + } + + public function findByDynamicType(): void + { + $items = $this->repository->findBy(['blah' => 'testing']); + + foreach ($items as $test) { + $test->doSomething(); + } + } +} + +/** + * @ORM\Entity() + */ +class MyEntity +{ + /** + * @ORM\Id() + * @ORM\GeneratedValue() + * @ORM\Column(type="integer") + * + * @var int + */ + private $id; + + public function doSomething(): void + { + } +} From 2c6400376fab08d91cc17105e28da19726d0ae59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 18:10:09 +0200 Subject: [PATCH 4/7] Add integration tests for entity manager merge return --- .../ORM/EntityManagerIntegrationTest.php | 1 + .../ORM/data/entityManagerMergeReturn.php | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/DoctrineIntegration/ORM/data/entityManagerMergeReturn.php diff --git a/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php index b41bf007..a6353a59 100644 --- a/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php +++ b/tests/DoctrineIntegration/ORM/EntityManagerIntegrationTest.php @@ -15,6 +15,7 @@ public function dataTopics(): array return [ ['entityManagerDynamicReturn'], ['entityRepositoryDynamicReturn'], + ['entityManagerMergeReturn'], ]; } diff --git a/tests/DoctrineIntegration/ORM/data/entityManagerMergeReturn.php b/tests/DoctrineIntegration/ORM/data/entityManagerMergeReturn.php new file mode 100644 index 00000000..b72a5df2 --- /dev/null +++ b/tests/DoctrineIntegration/ORM/data/entityManagerMergeReturn.php @@ -0,0 +1,44 @@ +entityManager = $entityManager; + } + + public function merge(): void + { + $test = $this->entityManager->merge(new MyEntity()); + $test->doSomething(); + } +} + +/** + * @ORM\Entity() + */ +class MyEntity +{ + /** + * @ORM\Id() + * @ORM\GeneratedValue() + * @ORM\Column(type="integer") + * + * @var int + */ + private $id; + + public function doSomething(): void + { + } +} From cd96d3219a576ebe02f83da8e7dfcc602d17ca63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 18:30:48 +0200 Subject: [PATCH 5/7] Use abstract names provided by doctrine/persistence So that things would be compatible with both ORM and ODM. --- extension.neon | 6 +++--- ....php => ObjectManagerFindDynamicReturnTypeExtension.php} | 2 +- ...bjectManagerGetRepositoryDynamicReturnTypeExtension.php} | 4 ++-- ...n.php => ObjectRepositoryDynamicReturnTypeExtension.php} | 6 +++--- .../{EntityRepositoryType.php => ObjectRepositoryType.php} | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) rename src/Type/Doctrine/{EntityManagerFindDynamicReturnTypeExtension.php => ObjectManagerFindDynamicReturnTypeExtension.php} (94%) rename src/Type/Doctrine/{EntityManagerGetRepositoryDynamicReturnTypeExtension.php => ObjectManagerGetRepositoryDynamicReturnTypeExtension.php} (90%) rename src/Type/Doctrine/{EntityRepositoryDynamicReturnTypeExtension.php => ObjectRepositoryDynamicReturnTypeExtension.php} (87%) rename src/Type/Doctrine/{EntityRepositoryType.php => ObjectRepositoryType.php} (92%) diff --git a/extension.neon b/extension.neon index 21e8e248..25fa2283 100644 --- a/extension.neon +++ b/extension.neon @@ -12,11 +12,11 @@ services: tags: - phpstan.broker.dynamicMethodReturnTypeExtension - - class: PHPStan\Type\Doctrine\EntityManagerFindDynamicReturnTypeExtension + class: PHPStan\Type\Doctrine\ObjectManagerFindDynamicReturnTypeExtension tags: - phpstan.broker.dynamicMethodReturnTypeExtension - - class: PHPStan\Type\Doctrine\EntityManagerGetRepositoryDynamicReturnTypeExtension + class: PHPStan\Type\Doctrine\ObjectManagerGetRepositoryDynamicReturnTypeExtension arguments: repositoryClass: %doctrine.repositoryClass% tags: @@ -26,6 +26,6 @@ services: tags: - phpstan.broker.dynamicMethodReturnTypeExtension - - class: PHPStan\Type\Doctrine\EntityRepositoryDynamicReturnTypeExtension + class: PHPStan\Type\Doctrine\ObjectRepositoryDynamicReturnTypeExtension tags: - phpstan.broker.dynamicMethodReturnTypeExtension diff --git a/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php b/src/Type/Doctrine/ObjectManagerFindDynamicReturnTypeExtension.php similarity index 94% rename from src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php rename to src/Type/Doctrine/ObjectManagerFindDynamicReturnTypeExtension.php index fa0884d6..f0c5fa26 100644 --- a/src/Type/Doctrine/EntityManagerFindDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/ObjectManagerFindDynamicReturnTypeExtension.php @@ -11,7 +11,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -class EntityManagerFindDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class ObjectManagerFindDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension { public function getClass(): string diff --git a/src/Type/Doctrine/EntityManagerGetRepositoryDynamicReturnTypeExtension.php b/src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php similarity index 90% rename from src/Type/Doctrine/EntityManagerGetRepositoryDynamicReturnTypeExtension.php rename to src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php index b383c18f..871f12b0 100644 --- a/src/Type/Doctrine/EntityManagerGetRepositoryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/ObjectManagerGetRepositoryDynamicReturnTypeExtension.php @@ -10,7 +10,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; -class EntityManagerGetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class ObjectManagerGetRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension { /** @var string */ @@ -47,7 +47,7 @@ public function getTypeFromMethodCall( return new MixedType(); } - return new EntityRepositoryType($argType->getValue(), $this->repositoryClass); + return new ObjectRepositoryType($argType->getValue(), $this->repositoryClass); } } diff --git a/src/Type/Doctrine/EntityRepositoryDynamicReturnTypeExtension.php b/src/Type/Doctrine/ObjectRepositoryDynamicReturnTypeExtension.php similarity index 87% rename from src/Type/Doctrine/EntityRepositoryDynamicReturnTypeExtension.php rename to src/Type/Doctrine/ObjectRepositoryDynamicReturnTypeExtension.php index b542508c..be363648 100644 --- a/src/Type/Doctrine/EntityRepositoryDynamicReturnTypeExtension.php +++ b/src/Type/Doctrine/ObjectRepositoryDynamicReturnTypeExtension.php @@ -12,12 +12,12 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; -class EntityRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension +class ObjectRepositoryDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension { public function getClass(): string { - return 'Doctrine\ORM\EntityRepository'; + return 'Doctrine\Common\Persistence\ObjectRepository'; } public function isMethodSupported(MethodReflection $methodReflection): bool @@ -36,7 +36,7 @@ public function getTypeFromMethodCall( ): Type { $calledOnType = $scope->getType($methodCall->var); - if (!$calledOnType instanceof EntityRepositoryType) { + if (!$calledOnType instanceof ObjectRepositoryType) { return new MixedType(); } $methodName = $methodReflection->getName(); diff --git a/src/Type/Doctrine/EntityRepositoryType.php b/src/Type/Doctrine/ObjectRepositoryType.php similarity index 92% rename from src/Type/Doctrine/EntityRepositoryType.php rename to src/Type/Doctrine/ObjectRepositoryType.php index 49c8c510..f5538b35 100644 --- a/src/Type/Doctrine/EntityRepositoryType.php +++ b/src/Type/Doctrine/ObjectRepositoryType.php @@ -5,7 +5,7 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\VerbosityLevel; -class EntityRepositoryType extends ObjectType +class ObjectRepositoryType extends ObjectType { /** @var string */ From 35548c71c3a5eb6254766d57bfe9b33fcc0b1c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 19:16:03 +0200 Subject: [PATCH 6/7] Add ODM to require-dev With the necessary dependencies on Travis-CI to ensure the build will run properly. Following the same approach as https://github.com/doctrine/mongodb/blob/1.6.3/.travis.yml#L61 (for PHP 7+ installation). --- .travis.yml | 1 + composer.json | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7f46b336..06f69511 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ php: - 7.1 - 7.2 before_script: + - pecl install -f mongodb-stable - composer self-update - composer install script: diff --git a/composer.json b/composer.json index 6b438d95..e7991569 100644 --- a/composer.json +++ b/composer.json @@ -25,12 +25,15 @@ "slevomat/coding-standard": "^4.5.2", "doctrine/common": "^2.7", "doctrine/orm": "^2.5", - "doctrine/collections": "^1.0" + "doctrine/collections": "^1.0", + "doctrine/mongodb-odm": "^1.2", + "alcaeus/mongo-php-adapter": "^1.1" }, "conflict": { "doctrine/common": "<2.7", "doctrine/orm": "<2.5", - "doctrine/collections": "<1.0" + "doctrine/collections": "<1.0", + "doctrine/mongodb-odm": "<1.2" }, "autoload": { "psr-4": { @@ -39,5 +42,10 @@ }, "autoload-dev": { "classmap": ["tests/"] + }, + "config": { + "platform": { + "ext-mongo": "1.6.16" + } } } From af7bb4297e766441d050410781418eecb0056acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 18 Aug 2018 19:16:50 +0200 Subject: [PATCH 7/7] Add integration tests for ODM features as well --- .../ODM/DocumentManagerIntegrationTest.php | 37 +++++++++ .../ODM/data/documentManagerDynamicReturn.php | 71 +++++++++++++++++ .../ODM/data/documentManagerMergeReturn.php | 43 ++++++++++ .../data/documentRepositoryDynamicReturn.php | 79 +++++++++++++++++++ tests/DoctrineIntegration/ODM/phpstan.neon | 6 ++ 5 files changed, 236 insertions(+) create mode 100644 tests/DoctrineIntegration/ODM/DocumentManagerIntegrationTest.php create mode 100644 tests/DoctrineIntegration/ODM/data/documentManagerDynamicReturn.php create mode 100644 tests/DoctrineIntegration/ODM/data/documentManagerMergeReturn.php create mode 100644 tests/DoctrineIntegration/ODM/data/documentRepositoryDynamicReturn.php create mode 100644 tests/DoctrineIntegration/ODM/phpstan.neon diff --git a/tests/DoctrineIntegration/ODM/DocumentManagerIntegrationTest.php b/tests/DoctrineIntegration/ODM/DocumentManagerIntegrationTest.php new file mode 100644 index 00000000..f46b795b --- /dev/null +++ b/tests/DoctrineIntegration/ODM/DocumentManagerIntegrationTest.php @@ -0,0 +1,37 @@ +documentManager = $documentManager; + } + + public function findDynamicType(): void + { + $test = $this->documentManager->find(MyDocument::class, 'blah-123'); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function getReferenceDynamicType(): void + { + $test = $this->documentManager->getReference(MyDocument::class, 'blah-123'); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function getPartialReferenceDynamicType(): void + { + $test = $this->documentManager->getPartialReference(MyDocument::class, 'blah-123'); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } +} + +/** + * @Document() + */ +class MyDocument +{ + /** + * @Id(strategy="NONE", type="string") + * + * @var string + */ + private $id; + + public function doSomething(): void + { + } +} diff --git a/tests/DoctrineIntegration/ODM/data/documentManagerMergeReturn.php b/tests/DoctrineIntegration/ODM/data/documentManagerMergeReturn.php new file mode 100644 index 00000000..4a190fe1 --- /dev/null +++ b/tests/DoctrineIntegration/ODM/data/documentManagerMergeReturn.php @@ -0,0 +1,43 @@ +documentManager = $documentManager; + } + + public function merge(): void + { + $test = $this->documentManager->merge(new MyDocument()); + $test->doSomething(); + } +} + +/** + * @Document() + */ +class MyDocument +{ + /** + * @Id(strategy="NONE", type="string") + * + * @var string + */ + private $id; + + public function doSomething(): void + { + } +} diff --git a/tests/DoctrineIntegration/ODM/data/documentRepositoryDynamicReturn.php b/tests/DoctrineIntegration/ODM/data/documentRepositoryDynamicReturn.php new file mode 100644 index 00000000..590bdedc --- /dev/null +++ b/tests/DoctrineIntegration/ODM/data/documentRepositoryDynamicReturn.php @@ -0,0 +1,79 @@ +repository = $documentManager->getRepository(MyDocument::class); + } + + public function findDynamicType(): void + { + $test = $this->repository->find(1); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function findOneByDynamicType(): void + { + $test = $this->repository->findOneBy(['blah' => 'testing']); + + if ($test === null) { + throw new RuntimeException('Sorry, but no...'); + } + + $test->doSomething(); + } + + public function findAllDynamicType(): void + { + $items = $this->repository->findAll(); + + foreach ($items as $test) { + $test->doSomething(); + } + } + + public function findByDynamicType(): void + { + $items = $this->repository->findBy(['blah' => 'testing']); + + foreach ($items as $test) { + $test->doSomething(); + } + } +} + +/** + * @Document() + */ +class MyDocument +{ + /** + * @Id(strategy="NONE", type="string") + * + * @var string + */ + private $id; + + public function doSomething(): void + { + } +} diff --git a/tests/DoctrineIntegration/ODM/phpstan.neon b/tests/DoctrineIntegration/ODM/phpstan.neon new file mode 100644 index 00000000..446f214a --- /dev/null +++ b/tests/DoctrineIntegration/ODM/phpstan.neon @@ -0,0 +1,6 @@ +includes: + - ../../../extension.neon + +parameters: + doctrine: + repositoryClass: Doctrine\ODM\MongoDB\DocumentRepository