diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index abe0f95959..b352a21538 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -24,4 +24,4 @@ on: jobs: coding-standards: - uses: "doctrine/.github/.github/workflows/coding-standards.yml@12.1.0" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@v12.2.0" diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml new file mode 100644 index 0000000000..2b141c2b23 --- /dev/null +++ b/.github/workflows/composer-lint.yml @@ -0,0 +1,20 @@ +name: "Composer Lint" + +on: + pull_request: + branches: + - "*.x" + paths: + - ".github/workflows/composer-lint.yml" + - "composer.json" + push: + branches: + - "*.x" + paths: + - ".github/workflows/composer-lint.yml" + - "composer.json" + +jobs: + composer-lint: + name: "Composer Lint" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@v12.2.0" diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 5a34474b36..f99319c4b4 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -17,4 +17,4 @@ on: jobs: documentation: name: "Documentation" - uses: "doctrine/.github/.github/workflows/documentation.yml@12.1.0" + uses: "doctrine/.github/.github/workflows/documentation.yml@v12.2.0" diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 0fd9b0c667..4e1786168e 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -7,7 +7,7 @@ on: jobs: release: - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@12.1.0" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@v12.2.0" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/composer.json b/composer.json index b7f39c10c9..ccf22b1f1a 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,39 @@ { "name": "doctrine/orm", - "type": "library", "description": "Object-Relational-Mapper for PHP", - "keywords": ["orm", "database"], - "homepage": "https://www.doctrine-project.org/projects/orm.html", "license": "MIT", - "authors": [ - {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, - {"name": "Roman Borschel", "email": "roman@code-factory.org"}, - {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, - {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, - {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} + "type": "library", + "keywords": [ + "orm", + "database" ], - "scripts": { - "docs": "composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args" - }, - "config": { - "allow-plugins": { - "composer/package-versions-deprecated": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "phpstan/extension-installer": true + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, - "sort-packages": true - }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "homepage": "https://www.doctrine-project.org/projects/orm.html", "require": { "php": "^7.1 || ^8.0", - "composer-runtime-api": "^2", "ext-ctype": "*", + "composer-runtime-api": "^2", "doctrine/cache": "^1.12.1 || ^2.1.1", "doctrine/collections": "^1.5 || ^2.1", "doctrine/common": "^3.0.3", @@ -47,7 +54,7 @@ "doctrine/coding-standard": "^9.0.2 || ^14.0", "phpbench/phpbench": "^0.16.10 || ^1.0", "phpstan/extension-installer": "~1.1.0 || ^1.4", - "phpstan/phpstan": "~1.4.10 || 2.1.22", + "phpstan/phpstan": "~1.4.10 || 2.1.23", "phpstan/phpstan-deprecation-rules": "^1 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", @@ -64,17 +71,29 @@ "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" }, "autoload": { - "psr-4": { "Doctrine\\ORM\\": "src" } + "psr-4": { + "Doctrine\\ORM\\": "src" + } }, "autoload-dev": { "psr-4": { - "Doctrine\\Tests\\": "tests/Tests", + "Doctrine\\Performance\\": "tests/Performance", "Doctrine\\StaticAnalysis\\": "tests/StaticAnalysis", - "Doctrine\\Performance\\": "tests/Performance" + "Doctrine\\Tests\\": "tests/Tests" } }, - "bin": ["bin/doctrine"], - "archive": { - "exclude": ["!vendor", "tests", "*phpunit.xml", "build.xml", "build.properties", "composer.phar", "vendor/satooshi", "lib/vendor", "*.swp"] + "bin": [ + "bin/doctrine" + ], + "config": { + "allow-plugins": { + "composer/package-versions-deprecated": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true + }, + "sort-packages": true + }, + "scripts": { + "docs": "composer --working-dir docs update && ./docs/vendor/bin/build-docs.sh @additional_args" } } diff --git a/docs/en/index.rst b/docs/en/index.rst index 190c9dc229..f6e7e7e496 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -13,7 +13,6 @@ If this documentation is not helping to answer questions you have about Doctrine ORM don't panic. You can get help from different sources: - There is a :doc:`FAQ ` with answers to frequent questions. -- The `Doctrine Mailing List `_ - Slack chat room `#orm `_ - Report a bug on `GitHub `_. - On `StackOverflow `_ diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0b966b47b7..897178a71c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1056,6 +1056,12 @@ parameters: count: 1 path: src/Internal/HydrationCompleteHandler.php + - + message: '#^Offset int\|null might not exist on array\\.$#' + identifier: offsetAccess.notFound + count: 1 + path: src/Internal/StronglyConnectedComponents.php + - message: '#^Property Doctrine\\ORM\\Internal\\StronglyConnectedComponents\:\:\$representingNodes \(array\\) does not accept array\\.$#' identifier: assign.propertyType @@ -3594,7 +3600,7 @@ parameters: - message: '#^Property Doctrine\\ORM\\Query\\Filter\\SQLFilter\:\:\$parameters \(array\\) does not accept non\-empty\-array\\.$#' identifier: assign.propertyType - count: 1 + count: 2 path: src/Query/Filter/SQLFilter.php - @@ -4173,12 +4179,6 @@ parameters: count: 1 path: src/Tools/Console/Command/ConvertMappingCommand.php - - - message: '#^Parameter \#2 \$destPath of method Doctrine\\ORM\\Tools\\Console\\Command\\ConvertMappingCommand\:\:getExporter\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Console/Command/ConvertMappingCommand.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' identifier: property.notFound @@ -4203,12 +4203,6 @@ parameters: count: 1 path: src/Tools/Console/Command/GenerateEntitiesCommand.php - - - message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityGenerator\:\:generate\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Console/Command/GenerateEntitiesCommand.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#' identifier: property.notFound @@ -4227,12 +4221,6 @@ parameters: count: 1 path: src/Tools/Console/Command/GenerateProxiesCommand.php - - - message: '#^Parameter \#2 \$proxyDir of method Doctrine\\ORM\\Proxy\\ProxyFactory\:\:generateProxyClasses\(\) expects string\|null, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Console/Command/GenerateProxiesCommand.php - - message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$customRepositoryClassName\.$#' identifier: property.notFound @@ -4251,12 +4239,6 @@ parameters: count: 1 path: src/Tools/Console/Command/GenerateRepositoriesCommand.php - - - message: '#^Parameter \#2 \$outputDirectory of method Doctrine\\ORM\\Tools\\EntityRepositoryGenerator\:\:writeEntityRepositoryClass\(\) expects string, string\|false given\.$#' - identifier: argument.type - count: 1 - path: src/Tools/Console/Command/GenerateRepositoriesCommand.php - - message: '#^Method Doctrine\\ORM\\Tools\\Console\\Command\\MappingDescribeCommand\:\:formatMappings\(\) has parameter \$propertyMappings with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue diff --git a/src/Persisters/Entity/BasicEntityPersister.php b/src/Persisters/Entity/BasicEntityPersister.php index ad29122300..75b89e2c39 100644 --- a/src/Persisters/Entity/BasicEntityPersister.php +++ b/src/Persisters/Entity/BasicEntityPersister.php @@ -1753,6 +1753,11 @@ public function getSelectConditionStatementSQL($field, $value, $assoc = null, $c $value = [$value]; } + if ($value === []) { + $selectedColumns[] = '1=0'; + continue; + } + $nullKeys = array_keys($value, null, true); $nonNullValues = array_diff_key($value, array_flip($nullKeys)); diff --git a/tests/Tests/ORM/Functional/Ticket/GH12254Test.php b/tests/Tests/ORM/Functional/Ticket/GH12254Test.php new file mode 100644 index 0000000000..6be7fefc4d --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH12254Test.php @@ -0,0 +1,62 @@ +setUpEntitySchema([ + GH12254EntityA::class, + ]); + + $this->_em->persist(new GH12254EntityA()); + $this->_em->flush(); + $this->_em->clear(); + } + + public function testFindByEmptyArrayShouldReturnEmptyArray(): void + { + // pretend we are starting afresh + $this->_em = $this->getEntityManager(); + $result = $this->_em->getRepository(GH12254EntityA::class)->findBy(['id' => []]); + $this->assertEmpty($result); + } + + public function testFindByInNullableField(): void + { + $this->_em = $this->getEntityManager(); + $result = $this->_em->getRepository(GH12254EntityA::class)->findBy(['name' => []]); + $this->assertEmpty($result); + } +} + +/** + * @Entity() + */ +class GH12254EntityA +{ + /** + * @Column(type="integer") + * @Id() + * @GeneratedValue(strategy="AUTO") + * @var int + */ + public $id; + + /** + * @Column(type="string", nullable=true) + * @var string|null + */ + public $name = null; +} diff --git a/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php b/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php index 60ed7391f3..d9e8fe96fe 100644 --- a/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php +++ b/tests/Tests/ORM/Persisters/BasicEntityPersisterTypeValueSqlTest.php @@ -129,7 +129,10 @@ public function testSelectConditionStatementNeqNull(): void self::assertEquals('test IS NOT NULL', $statement); } - /** @group DDC-3056 */ + /** + * @group DDC-3056 + * @group GH12254 + */ public function testSelectConditionStatementWithMultipleValuesContainingNull(): void { self::assertEquals( @@ -151,6 +154,11 @@ public function testSelectConditionStatementWithMultipleValuesContainingNull(): '(t0.id IN (?, ?) OR t0.id IS NULL)', $this->persister->getSelectConditionStatementSQL('id', [123, null, 234]) ); + + self::assertEquals( + '1=0', + $this->persister->getSelectConditionStatementSQL('id', []) + ); } public function testCountCondition(): void