diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..15bd1729 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "CI" diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 3840c425..69840910 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -12,6 +12,6 @@ on: jobs: coding-standards: name: "Coding Standards" - uses: "doctrine/.github/.github/workflows/coding-standards.yml@1.5.0" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1" with: php-version: "8.1" diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml index 0fc7d3d8..89aaebb7 100644 --- a/.github/workflows/composer-lint.yml +++ b/.github/workflows/composer-lint.yml @@ -15,6 +15,6 @@ on: jobs: composer-lint: name: "Composer Lint" - uses: "doctrine/.github/.github/workflows/composer-lint.yml@1.5.0" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@5.0.1" with: php-version: "8.1" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ca759949..3e1c4e3c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,6 +13,8 @@ on: jobs: phpunit: name: "PHPUnit" - uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.5.0" + uses: "doctrine/.github/.github/workflows/continuous-integration.yml@5.0.1" with: php-versions: '["8.1"]' + secrets: + CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 2e875895..634f9ab7 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "Checkout code" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Install PHP" uses: "shivammathur/setup-php@v2" @@ -36,7 +36,7 @@ jobs: run: "composer require --dev phpdocumentor/guides-cli --no-update" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "highest" diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 5ee7db92..3cac620a 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,7 +8,7 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@1.5.0" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 66079033..45924c15 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -12,6 +12,6 @@ on: jobs: static-analysis: name: "Static Analysis" - uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.5.0" + uses: "doctrine/.github/.github/workflows/static-analysis.yml@5.0.1" with: php-version: "8.1" diff --git a/docs/en/expression-builder.rst b/docs/en/expression-builder.rst index e0224418..043b89cc 100644 --- a/docs/en/expression-builder.rst +++ b/docs/en/expression-builder.rst @@ -124,6 +124,16 @@ isNull $collection->matching(new Criteria($expression)); +isNotNull +--------- + +.. code-block:: php + $expressionBuilder = Criteria::expr(); + + $expression = $expressionBuilder->isNotNull('foo'); + + $collection->matching(new Criteria($expression)); + in --- diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 11304c9c..ddd04ce5 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -2,6 +2,7 @@ parameters: level: 8 paths: - src + - tests/StaticAnalysis ignoreErrors: # https://github.com/phpstan/phpstan-src/pull/1289 diff --git a/psalm.xml.dist b/psalm.xml.dist index bf24a173..aaa96c85 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -9,6 +9,7 @@ > + diff --git a/src/ArrayCollection.php b/src/ArrayCollection.php index 86b07039..4039c5d3 100644 --- a/src/ArrayCollection.php +++ b/src/ArrayCollection.php @@ -314,6 +314,8 @@ public function reduce(Closure $func, $initial = null): mixed /** * {@inheritDoc} * + * @psalm-param Closure(T, TKey):bool $p + * * @return static * @psalm-return static */ diff --git a/src/Collection.php b/src/Collection.php index 3dc56ac3..93f80442 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -90,6 +90,8 @@ public function map(Closure $func): self; /** * {@inheritDoc} * + * @psalm-param Closure(T, TKey):bool $p + * * @return Collection A collection with the results of the filter operation. * @psalm-return Collection */ @@ -97,7 +99,9 @@ public function filter(Closure $p): self; /** * {@inheritDoc} - + * + * @psalm-param Closure(TKey, T):bool $p + * * @return Collection[] An array with two elements. The first element contains the collection * of elements where the predicate returned TRUE, the second element * contains the collection of elements where the predicate returned FALSE. diff --git a/src/ExpressionBuilder.php b/src/ExpressionBuilder.php index 09145c12..4a504f5f 100644 --- a/src/ExpressionBuilder.php +++ b/src/ExpressionBuilder.php @@ -68,6 +68,11 @@ public function isNull(string $field): Comparison return new Comparison($field, Comparison::EQ, new Value(null)); } + public function isNotNull(string $field): Comparison + { + return new Comparison($field, Comparison::NEQ, new Value(null)); + } + /** @param mixed[] $values */ public function in(string $field, array $values): Comparison { diff --git a/tests/ExpressionBuilderTest.php b/tests/ExpressionBuilderTest.php index cfba7626..1c272938 100644 --- a/tests/ExpressionBuilderTest.php +++ b/tests/ExpressionBuilderTest.php @@ -114,6 +114,14 @@ public function testIsNull(): void self::assertEquals(Comparison::EQ, $expr->getOperator()); } + public function testIsNotNull(): void + { + $expr = $this->builder->isNotNull('a'); + + self::assertInstanceOf(Comparison::class, $expr); + self::assertEquals(Comparison::NEQ, $expr->getOperator()); + } + public function testContains(): void { $expr = $this->builder->contains('a', 'b'); diff --git a/tests/StaticAnalysis/CustomCollection.php b/tests/StaticAnalysis/CustomCollection.php new file mode 100644 index 00000000..ae8da930 --- /dev/null +++ b/tests/StaticAnalysis/CustomCollection.php @@ -0,0 +1,46 @@ + + */ +abstract class CustomCollection implements Collection +{ + /** @var ArrayCollection */ + private ArrayCollection $collection; + + /** @param ArrayCollection $arrayCollection */ + public function __construct(ArrayCollection $arrayCollection) + { + $this->collection = $arrayCollection; + } + + /** + * @psalm-param Closure(T, TKey):bool $p + * + * @return Collection + */ + public function filter(Closure $p): Collection + { + return $this->collection->filter($p); + } + + /** + * @psalm-param Closure(TKey, T):bool $p + * + * @psalm-return array{0: Collection, 1: Collection} + */ + public function partition(Closure $p): array + { + return $this->collection->partition($p); + } +}