diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 98f0d9d..00064f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,8 +17,6 @@ jobs: strategy: matrix: php-version: - - '7.2' - - '7.3' - '7.4' - '8.0' - '8.1' @@ -26,8 +24,6 @@ jobs: - '8.3' coverage: ['pcov'] dependencies: [''] - include: - - { php-version: '7.1', coverage: 'xdebug', dependencies: '' } steps: - name: Checkout code @@ -67,41 +63,12 @@ jobs: run: | php vendor/bin/php-coveralls -v - coding-standards: - name: Coding Standards - runs-on: ubuntu-latest - - env: - PHP_VERSION: '8.1' - PHP_CS_FIXER_VERSION: v3.16.0 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ env.PHP_VERSION }} - tools: php-cs-fixer:${{ env.PHP_CS_FIXER_VERSION }} - - - name: Restore PHP-CS-Fixer cache - uses: actions/cache@v3 - with: - path: .php_cs.cache - key: "php-cs-fixer" - restore-keys: "php-cs-fixer" - - - name: Run PHP-CS-Fixer, version ${{ env.PHP_CS_FIXER_VERSION }} - run: | - php-cs-fixer fix --diff --dry-run --verbose - analyze: name: Static Analysis and Validation runs-on: ubuntu-latest env: - PHP_VERSION: '8.1' + PHP_VERSION: '8.2' COMPOSER_ROOT_VERSION: v5.99 steps: @@ -125,6 +92,15 @@ jobs: composer-${{ env.PHP_VERSION }}- composer- + - name: Restore PHP-CS-Fixer cache + uses: actions/cache@v3 + with: + path: .php_cs.cache + key: php-cs-fixer-${{ env.PHP_VERSION }}-${{ hashFiles('composer.*') }} + restore-keys: | + php-cs-fixer-${{ env.PHP_VERSION }}- + php-cs-fixer- + - name: Install dependencies run: | composer update --prefer-dist --no-interaction --no-progress diff --git a/.gitignore b/.gitignore index 483ab79..f55f9fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /composer.lock /vendor /build -/.phpunit.result.cache +*.cache +.?* diff --git a/.phan/config.php b/.phan/config.php index 53c8d56..c7b633e 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -3,7 +3,7 @@ use Phan\Issue; return [ - 'target_php_version' => '7.1', + 'target_php_version' => '7.4', 'backward_compatibility_checks' => false, 'exclude_analysis_directory_list' => [ 'vendor/', diff --git a/README.md b/README.md index bf83192..3b30942 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This rigorously tested library just works. Pipeline neither defines nor throws a composer require sanmai/pipeline -The latest version requires PHP 7.1 or above, including PHP 8.0 and later. +The latest version requires PHP 7.4 or above, including PHP 8.0 and later. There are earlier versions that work under PHP 5.6 and above, but they are not as feature complete. diff --git a/composer.json b/composer.json index 4229b13..a3ce3ed 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require": { - "php": "^7.1 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.8", diff --git a/src/Standard.php b/src/Standard.php index f3302a6..8e1486a 100644 --- a/src/Standard.php +++ b/src/Standard.php @@ -59,14 +59,12 @@ class Standard implements IteratorAggregate, Countable * * @var ?iterable */ - private $pipeline; + private ?iterable $pipeline; /** * Contructor with an optional source of data. - * - * @param ?iterable $input */ - public function __construct(iterable $input = null) + public function __construct(?iterable $input = null) { // IteratorAggregate is a nuance best we avoid dealing with. // For example, CallbackFilterIterator needs a plain Iterator. @@ -79,10 +77,8 @@ public function __construct(iterable $input = null) /** * Appends the contents of an interable to the end of the pipeline. - * - * @param ?iterable $values */ - public function append(iterable $values = null): self + public function append(?iterable $values = null): self { // Do we need to do anything here? if ($this->willReplace($values)) { @@ -108,10 +104,8 @@ public function push(...$vector): self /** * Prepends the pipeline with the contents of an iterable. - * - * @param ?iterable $values */ - public function prepend(iterable $values = null): self + public function prepend(?iterable $values = null): self { // Do we need to do anything here? if ($this->willReplace($values)) { @@ -140,7 +134,7 @@ public function unshift(...$vector): self * * Utility method for appending/prepending methods. */ - private function willReplace(iterable $values = null): bool + private function willReplace(?iterable $values = null): bool { // Nothing needs to be done here. /** @phan-suppress-next-line PhanTypeComparisonFromArray */ @@ -263,7 +257,7 @@ public function chunk(int $length, bool $preserve_keys = false): self } /** - * @psalm-param positive-int $length + * @psalm-param positive-int $length */ private static function toChunks(Generator $input, int $length, bool $preserve_keys): iterable { @@ -295,14 +289,15 @@ public function map(?callable $func = null): self } // Let's check what we got for a start. - $this->pipeline = $func(); + $value = $func(); // Generator is a generator, moving along - if ($this->pipeline instanceof Generator) { + if ($value instanceof Generator) { // It is possible to detect if callback is a generator like so: // (new \ReflectionFunction($func))->isGenerator(); // Yet this will restrict users from replacing the pipeline and has unknown performance impact. // But, again, we could add a direct internal method to replace the pipeline, e.g. as done by unpack() + $this->pipeline = $value; return $this; } @@ -311,7 +306,7 @@ public function map(?callable $func = null): self // We do not cast to an array here because casting a null to an array results in // an empty array; that's surprising and not how it works for other values. $this->pipeline = [ - $this->pipeline, + $value, ]; return $this; @@ -343,7 +338,7 @@ private static function apply(iterable $previous, callable $func): iterable * * @return $this */ - public function cast(callable $func = null): self + public function cast(?callable $func = null): self { if (null === $func) { return $this; diff --git a/tests/StaticAnalysisTest.php b/tests/StaticAnalysisTest.php index 2cd79d5..0d529bb 100644 --- a/tests/StaticAnalysisTest.php +++ b/tests/StaticAnalysisTest.php @@ -19,10 +19,12 @@ namespace Tests\Pipeline; +use ArrayIterator; use PHPUnit\Framework\TestCase; use Pipeline\Standard; use ReflectionClass; use ReflectionMethod; +use Traversable; /** * @coversNothing @@ -63,4 +65,126 @@ public function testAllMethodsArePublicOrPrivate(ReflectionMethod $method): void $this->assertFalse($method->isProtected()); $this->assertTrue($method->isPublic() || $method->isPrivate()); } + + /** + * Test the interface is compatible with PHP 7.1 variety. + */ + public function testInterfaceCompatibilityPHP71(): void + { + $example = new class() extends Standard { + private $input; + + public function __construct(iterable $input = null) + { + $this->input = $input; + } + + public function append(iterable $values = null): self + { + return $this; + } + + public function push(...$vector): self + { + return $this; + } + + public function prepend(iterable $values = null): self + { + return $this; + } + + public function unshift(...$vector): self + { + return $this; + } + + public function flatten(): self + { + return $this; + } + + public function unpack(?callable $func = null): self + { + return $this; + } + + public function chunk(int $length, bool $preserve_keys = false): self + { + return $this; + } + + public function map(?callable $func = null): self + { + return $this; + } + + public function cast(callable $func = null): self + { + return $this; + } + + public function filter(?callable $func = null): self + { + return $this; + } + + public function reduce(?callable $func = null, $initial = null) + { + return null; + } + + public function fold($initial, ?callable $func = null) + { + return null; + } + + public function getIterator(): Traversable + { + return new ArrayIterator([]); + } + + public function toArray(bool $preserve_keys = false): array + { + return []; + } + + public function count(): int + { + return 0; + } + + public function slice(int $offset, ?int $length = null) + { + return $this; + } + + public function zip(iterable ...$inputs) + { + return $this; + } + + public function reservoir(int $size, ?callable $weightFunc = null): array + { + return []; + } + + public function min() + { + return 0; + } + + public function max() + { + return 0; + } + + public function flip() + { + return $this; + } + }; + + $this->assertSame(0, $example->count()); + } }