diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 862be75..f673d86 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -32,3 +32,27 @@ jobs: - name: "Run a static analysis with phpstan/phpstan" run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr" + + static-analysis-psalm: + name: "Static Analysis with Psalm" + runs-on: "ubuntu-20.04" + + strategy: + matrix: + php-version: + - "7.3" + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Psalm + uses: docker://vimeo/psalm-github-actions:4.4.1 + with: + composer_require_dev: true + security_analysis: true + report_file: results.sarif + - name: Upload Security Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index 6f24d73..56ed85a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,6 @@ /composer.lock /phpcs.xml /phpstan.neon +/psalm.xml /phpunit.xml /vendor/ diff --git a/README.md b/README.md index 890c809..4b615dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![Latest Stable Version](https://poser.pugx.org/bentools/iterable-functions/v/stable)](https://packagist.org/packages/bentools/iterable-functions) [![GitHub Actions][GA master image]][GA master] [![Code Coverage][Coverage image]][CodeCov Master] +[![Shepherd Type][Shepherd Image]][Shepherd Link] [![Total Downloads](https://poser.pugx.org/bentools/iterable-functions/downloads)](https://packagist.org/packages/bentools/iterable-functions) Iterable functions @@ -177,7 +178,14 @@ Unit tests php vendor/bin/pest ``` -[CodeCov Master]: https://codecov.io/gh/bpolaszek/php-iterable-functions/branch/2.0.x-dev -[Coverage image]: https://codecov.io/gh/bpolaszek/php-iterable-functions/branch/2.0.x-dev/graph/badge.svg [GA master]: https://github.com/bpolaszek/php-iterable-functions/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.0.x-dev + [GA master image]: https://github.com/bpolaszek/php-iterable-functions/workflows/Continuous%20Integration/badge.svg + +[CodeCov Master]: https://codecov.io/gh/bpolaszek/php-iterable-functions/branch/2.0.x-dev + +[Coverage image]: https://codecov.io/gh/bpolaszek/php-iterable-functions/branch/2.0.x-dev/graph/badge.svg + +[Shepherd Image]: https://shepherd.dev/github/bpolaszek/php-iterable-functions/coverage.svg + +[Shepherd Link]: https://shepherd.dev/github/bpolaszek/php-iterable-functions diff --git a/composer.json b/composer.json index b6a5021..7730355 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^0.12.67", "phpstan/phpstan-strict-rules": "^0.12.9", - "symfony/var-dumper": "^5.2" + "symfony/var-dumper": "^5.2", + "vimeo/psalm": "^4.4" }, "config": { "sort-packages": true diff --git a/psalm.xml.dist b/psalm.xml.dist new file mode 100644 index 0000000..3e6a115 --- /dev/null +++ b/psalm.xml.dist @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/iterable-functions.php b/src/iterable-functions.php index c35732c..f9a698d 100644 --- a/src/iterable-functions.php +++ b/src/iterable-functions.php @@ -73,9 +73,11 @@ function iterable_to_traversable(iterable $iterable): Traversable function iterable_filter(iterable $iterable, ?callable $filter = null) { if ($filter === null) { - $filter = static function ($value): bool { - return (bool) $value; - }; + $filter = + /** @param mixed $value */ + static function ($value): bool { + return (bool) $value; + }; } if ($iterable instanceof Traversable) { diff --git a/tests/IterableFilterTest.php b/tests/IterableFilterTest.php index 083d355..c1a44c5 100644 --- a/tests/IterableFilterTest.php +++ b/tests/IterableFilterTest.php @@ -23,7 +23,9 @@ it('filters an array with a callback', function (): void { $iterable = ['foo', 'bar']; - $filter = static function ($input): bool { + $filter = + /** @param mixed $input */ + static function ($input): bool { return $input === 'bar'; }; assertEquals([1 => 'bar'], iterable_to_array(iterable_filter($iterable, $filter))); @@ -31,7 +33,9 @@ it('filters a Travsersable object with a callback', function (): void { $iterable = SplFixedArray::fromArray(['foo', 'bar']); - $filter = static function ($input): bool { + $filter = + /** @param mixed $input */ + static function ($input): bool { return $input === 'bar'; }; assertEquals([1 => 'bar'], iterable_to_array(iterable_filter($iterable, $filter))); diff --git a/tests/IterableObjectTest.php b/tests/IterableObjectTest.php index f7dc96b..3eee1f5 100644 --- a/tests/IterableObjectTest.php +++ b/tests/IterableObjectTest.php @@ -17,9 +17,11 @@ $dataProvider = static function (): Generator { $data = ['foo', 'bar']; - $filter = static function ($value): bool { - return $value === 'bar'; - }; + $filter = + /** @param mixed $value */ + static function ($value): bool { + return $value === 'bar'; + }; $map = 'strtoupper'; yield from [ @@ -50,32 +52,50 @@ ]; }; -test('input: array | output: traversable', function ($data, $filter, $map, $expectedResult): void { - $iterableObject = iterable($data, $filter, $map); - assertEquals($expectedResult, iterator_to_array($iterableObject)); -})->with($dataProvider()); +test( + 'input: array | output: traversable', + /** @param array $data */ + function (array $data, ?callable $filter, ?callable $map, array $expectedResult): void { + $iterableObject = iterable($data, $filter, $map); + assertEquals($expectedResult, iterator_to_array($iterableObject)); + } +)->with($dataProvider()); -test('input: array | output: array', function ($data, $filter, $map, $expectedResult): void { - $iterableObject = iterable($data, $filter, $map); - assertEquals($expectedResult, $iterableObject->asArray()); -})->with($dataProvider()); +test( + 'input: array | output: array', + /** @param array $data */ + function (array $data, ?callable $filter, ?callable $map, array $expectedResult): void { + $iterableObject = iterable($data, $filter, $map); + assertEquals($expectedResult, $iterableObject->asArray()); + } +)->with($dataProvider()); -test('input: traversable | output: traversable', function ($data, $filter, $map, $expectedResult): void { - $data = SplFixedArray::fromArray($data); - $iterableObject = iterable($data, $filter, $map); - assertEquals($expectedResult, iterator_to_array($iterableObject)); -})->with($dataProvider()); +test( + 'input: traversable | output: traversable', + /** @param array $data */ + function (array $data, ?callable $filter, ?callable $map, array $expectedResult): void { + $data = SplFixedArray::fromArray($data); + $iterableObject = iterable($data, $filter, $map); + assertEquals($expectedResult, iterator_to_array($iterableObject)); + } +)->with($dataProvider()); -test('input: traversable | output: array', function ($data, $filter, $map, $expectedResult): void { - $data = SplFixedArray::fromArray($data); - $iterableObject = iterable($data, $filter, $map); - assertEquals($expectedResult, $iterableObject->asArray()); -})->with($dataProvider()); +test( + 'input: traversable | output: array', + /** @param array $data */ + function (array $data, ?callable $filter, ?callable $map, array $expectedResult): void { + $data = SplFixedArray::fromArray($data); + $iterableObject = iterable($data, $filter, $map); + assertEquals($expectedResult, $iterableObject->asArray()); + } +)->with($dataProvider()); it('filters the subject', function (): void { - $filter = static function ($value): bool { - return $value === 'bar'; - }; + $filter = + /** @param mixed $value */ + static function ($value): bool { + return $value === 'bar'; + }; $iterableObject = iterable(['foo', 'bar'])->filter($filter); assertEquals([1 => 'bar'], iterator_to_array($iterableObject)); }); @@ -88,9 +108,11 @@ }); it('combines filter and map', function (): void { - $filter = static function ($value): bool { - return $value === 'bar'; - }; + $filter = + /** @param mixed $value */ + static function ($value): bool { + return $value === 'bar'; + }; $map = 'strtoupper'; $iterableObject = iterable(['foo', 'bar'])->map($map)->filter($filter); assertInstanceOf(IterableObject::class, $iterableObject); diff --git a/tests/IterableReduceTest.php b/tests/IterableReduceTest.php index f6ee50d..0001cc4 100644 --- a/tests/IterableReduceTest.php +++ b/tests/IterableReduceTest.php @@ -12,16 +12,16 @@ it('reduces an array', function (): void { $iterable = [1, 2]; - $reduce = static function ($carry, $item) { - return $carry + $item; + $reduce = static function (?int $carry, int $item): int { + return (int) $carry + $item; }; assertSame(3, iterable_reduce($iterable, $reduce, 0)); }); it('reduces an traversable', function (): void { $iterable = SplFixedArray::fromArray([1, 2]); - $reduce = static function ($carry, $item) { - return $carry + $item; + $reduce = static function (?int $carry, int $item): int { + return (int) $carry + $item; }; assertSame(3, iterable_reduce($iterable, $reduce, 0)); });