diff --git a/.cz.yaml b/.cz.yaml index 8a44208..245146b 100644 --- a/.cz.yaml +++ b/.cz.yaml @@ -2,4 +2,4 @@ commitizen: name: cz_conventional_commits tag_format: $version - version: 0.6.2 + version: 1.0.0 diff --git a/.github/workflows/php-latest.yml b/.github/workflows/php-latest.yml index 19744fb..6fa64bb 100644 --- a/.github/workflows/php-latest.yml +++ b/.github/workflows/php-latest.yml @@ -33,7 +33,7 @@ jobs: restore-keys: | ${{ runner.os }}-php- - name: Install dependencies - run: composer update --no-ansi --no-interaction --prefer-dist --no-progress --ignore-platform-req=ext-ast + run: composer install --no-ansi --no-interaction --prefer-dist --no-progress --ignore-platform-req=ext-ast - name: Run test suite run: composer ci:test-build diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml index 8372eb2..26d468f 100644 --- a/.github/workflows/pr-quality.yml +++ b/.github/workflows/pr-quality.yml @@ -17,7 +17,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 - name: Validate composer.json and composer.lock run: composer validate --strict @@ -31,7 +31,7 @@ jobs: restore-keys: | ${{ runner.os }}-php- - name: Install dependencies - run: composer update --no-ansi --no-interaction --prefer-dist --no-progress --ignore-platform-req=ext-ast && composer dumpautoload + run: composer install --no-ansi --no-interaction --prefer-dist --no-progress --ignore-platform-req=ext-ast && composer dumpautoload - name: Scanning for lint errors run: composer ci:lint diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f71054..c75261a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.0.0 (2025-01-19) + +### BREAKING CHANGE + +- CsvFileStorage::getByKey now returns ItemFound instead of Item. + +### Feat + +- use phpolar/storage 3.0 + ## 0.6.2 (2025-01-01) ## 0.6.1 (2023-07-03) diff --git a/README.md b/README.md index 3bd313c..648d764 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@

- + PHPolar logo

# PHPolar CSV File Storage - -[![Latest Stable Version](https://poser.pugx.org/phpolar/csv-file-storage/v)](https://packagist.org/packages/phpolar/csv-file-storage) [![Total Downloads](https://poser.pugx.org/phpolar/csv-file-storage/downloads)](https://packagist.org/packages/phpolar/csv-file-storage) [![PHP Version Require](https://poser.pugx.org/phpolar/csv-file-storage/require/php)](https://packagist.org/packages/phpolar/csv-file-storage) [![PHPMD](https://github.com/phpolar/csv-file-storage/actions/workflows/phpmd.yml/badge.svg)](https://github.com/phpolar/csv-file-storage/actions/workflows/phpmd.yml) [![PHP Build Latest and Nightly](https://github.com/phpolar/csv-file-storage/actions/workflows/php-latest.yml/badge.svg)](https://github.com/phpolar/csv-file-storage/actions/workflows/php-latest.yml) +[![Coverage Status](https://coveralls.io/repos/github/phpolar/csv-file-storage/badge.svg?branch=main)](https://coveralls.io/github/phpolar/csv-file-storage?branch=main) [![Latest Stable Version](https://poser.pugx.org/phpolar/csv-file-storage/v)](https://packagist.org/packages/phpolar/csv-file-storage) [![Total Downloads](https://poser.pugx.org/phpolar/csv-file-storage/downloads)](https://packagist.org/packages/phpolar/csv-file-storage) [![PHP Version Require](https://poser.pugx.org/phpolar/csv-file-storage/require/php)](https://packagist.org/packages/phpolar/csv-file-storage) [![PHPMD](https://github.com/phpolar/csv-file-storage/actions/workflows/phpmd.yml/badge.svg)](https://github.com/phpolar/csv-file-storage/actions/workflows/phpmd.yml) [![PHP Build Latest and Nightly](https://github.com/phpolar/csv-file-storage/actions/workflows/php-latest.yml/badge.svg)](https://github.com/phpolar/csv-file-storage/actions/workflows/php-latest.yml) ## Adds support for storing data on disk in CSV format diff --git a/composer.json b/composer.json index ca66c1f..5d82bb0 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,4 @@ { - "$schema": "https://getcomposer.org/schema.json", "name": "phpolar/csv-file-storage", "description": "Adds support for storing data on disk in CSV format.", "type": "library", @@ -30,8 +29,8 @@ } }, "require": { - "php": ">=8.1", - "phpolar/storage": "^2.0" + "php": ">= 8.2", + "phpolar/storage": "^3.0" }, "license": "MIT", "scripts": { diff --git a/composer.lock b/composer.lock index 56fb6dd..8ee2725 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fda532158d89dc655e12e8bf1f5a7a75", + "content-hash": "ade786ee9a5b80457ab0a8c897eb6fbc", "packages": [ { "name": "phpolar/storage", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/phpolar/storage.git", - "reference": "0c72e6ec91d7be7fd3f60adc1064949c54567c4c" + "reference": "eb2716c49a938b952d2787ca397dd643962491bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpolar/storage/zipball/0c72e6ec91d7be7fd3f60adc1064949c54567c4c", - "reference": "0c72e6ec91d7be7fd3f60adc1064949c54567c4c", + "url": "https://api.github.com/repos/phpolar/storage/zipball/eb2716c49a938b952d2787ca397dd643962491bd", + "reference": "eb2716c49a938b952d2787ca397dd643962491bd", "shasum": "" }, "require": { @@ -27,11 +27,11 @@ "ext-ast": "*", "ext-openssl": "*", "phan/phan": "^5.4", - "php-coveralls/php-coveralls": "^2.5", - "phpmd/phpmd": "^2.13", + "php-coveralls/php-coveralls": "^2.7.0", + "phpmd/phpmd": "^2.15.0", "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^10.0", - "squizlabs/php_codesniffer": "^3.7" + "phpunit/phpunit": "^11.4.4", + "squizlabs/php_codesniffer": "^3.11.1" }, "type": "library", "autoload": { @@ -46,9 +46,9 @@ "description": "A convenient tool to speed up the creation of data storage for application development.", "support": { "issues": "https://github.com/phpolar/storage/issues", - "source": "https://github.com/phpolar/storage/tree/2.0.0" + "source": "https://github.com/phpolar/storage/tree/3.0.0" }, - "time": "2023-09-02T23:34:51+00:00" + "time": "2025-01-19T01:50:21+00:00" } ], "packages-dev": [ @@ -4805,7 +4805,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.1" + "php": ">= 8.2" }, "platform-dev": { "ext-ast": "*", diff --git a/phpunit.ci.xml b/phpunit.ci.xml index 9b09c6f..73f4adb 100644 --- a/phpunit.ci.xml +++ b/phpunit.ci.xml @@ -1,26 +1,18 @@ + cacheDirectory=".phpunit.cache"> - - tests/unit - tests/unit - + - src + src - + + diff --git a/phpunit.dev.xml b/phpunit.dev.xml index 6a5711a..da40d44 100644 --- a/phpunit.dev.xml +++ b/phpunit.dev.xml @@ -1,19 +1,15 @@ - - tests/unit - tests/acceptance @@ -24,7 +20,7 @@ - + diff --git a/src/CsvFileStorage.php b/src/CsvFileStorage.php index 92f4afd..d95085b 100644 --- a/src/CsvFileStorage.php +++ b/src/CsvFileStorage.php @@ -34,7 +34,7 @@ final class CsvFileStorage extends AbstractStorage implements Countable */ private $writeStream; - private int|false $fileSize = 0; + private int $fileSize = 0; /** * The first line of the CSV file. @@ -107,7 +107,14 @@ public function commit(): void fputcsv($this->writeStream, [$record]); break; case is_array($record): - fputcsv($this->writeStream, $record); + /** + * @var array + */ + $arr = $record; + fputcsv( + $this->writeStream, + $arr, + ); break; default: throw new DomainException(self::INVALID_VALUE_MSG); @@ -137,7 +144,8 @@ private function convertObjVars(array $objVars): array */ public function count(): int { - return $this->getCount(); + $result = $this->getCount(); + return max($result, 0); } protected function load(): void @@ -162,11 +170,12 @@ protected function load(): void /** * @param string $needle - * @param ReflectionNamedType[] $namedTypes + * @param ReflectionNamedType[]|\ReflectionIntersectionType[] $types */ - private function containsType(string $needle, array $namedTypes): bool + private function containsType(string $needle, array $types): bool { - $haystack = array_map(static fn (ReflectionNamedType $type) => $type->getName(), $namedTypes); + /** @phan-suppress-next-line PhanPartialTypeMismatchArgument */ + $haystack = array_map(static fn (ReflectionNamedType $type) => $type->getName(), array_filter($types, static fn ($type) => $type instanceof ReflectionNamedType)); return in_array($needle, $haystack); } diff --git a/tests/unit/CsvFileStorageTest.php b/tests/unit/CsvFileStorageTest.php index 2835c28..59ae5f7 100644 --- a/tests/unit/CsvFileStorageTest.php +++ b/tests/unit/CsvFileStorageTest.php @@ -13,8 +13,8 @@ use Phpolar\Phpolar\Storage\ItemKey; use Phpolar\Phpolar\Storage\ItemNotFound; use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\TestDox; +use PHPUnit\Framework\Attributes\WithoutErrorHandler; use PHPUnit\Framework\TestCase; #[CoversClass(CsvFileStorage::class)] @@ -44,7 +44,7 @@ static function (string $filename): void { file_exists($filename) && unlink($filename); }, ); - unset(self::$filenames); + self::$filenames = []; } #[TestDox("Shall save objects to file")] @@ -62,7 +62,6 @@ public function test1a() } #[TestDox("Shall save objects with primary key to file")] - #[Group("me")] public function test1b() { $givenPrimaryKey = uniqid(); @@ -124,7 +123,7 @@ public function test4() #[TestDox("Shall not clear the internal data if data is on file, no headers exist, and load is called")] public function test5() { - $sut = new CsvFileStorage("tests/__fakes__/without-headers.csv"); + $sut = new CsvFileStorage(__DIR__ . "/../__fakes__/without-headers.csv"); $fromFile = $sut->getAll(); $this->assertNotEmpty($fromFile); } @@ -133,40 +132,37 @@ public function test5() public function test6() { $this->expectException(DomainException::class); - new CsvFileStorage("tests/__fakes__/empty-headers.csv"); + new CsvFileStorage(__DIR__ . "/../__fakes__/empty-headers.csv"); } #[TestDox("Shall throw an exception if storage contains object types but file has no headers")] public function test7() { $this->expectException(DomainException::class); - new CsvFileStorage("tests/__fakes__/empty-headers.csv", FakeValueObject::class); + new CsvFileStorage(__DIR__ . "/../__fakes__/empty-headers.csv", FakeValueObject::class); } #[TestDox("Shall throw an exception if stream does not exist")] + #[WithoutErrorHandler] public function test8() { - $warningHandler = static fn () => true; - set_error_handler($warningHandler, E_WARNING); $this->expectException(FileNotExistsException::class); - new CsvFileStorage("php://non-existing-stream-handle"); - restore_error_handler(); + @new CsvFileStorage("php://non-existing-stream-handle"); } #[TestDox("Shall load objects from file")] public function testaa() { - $sut = new CsvFileStorage("tests/__fakes__/object.csv", FakeValueObject::class); + $sut = new CsvFileStorage(__DIR__ . "/../__fakes__/object.csv", FakeValueObject::class); $itemKey = new ItemKey(0); $item = $sut->getByKey($itemKey); $this->assertNotInstanceOf(ItemNotFound::class, $item); } #[TestDox("Shall load objects with primary key from file")] - #[Group("me")] public function testab() { - $sut = new CsvFileStorage("tests/__fakes__/object-with-pkey.csv", FakeValueObjectWithPrimaryKey::class); + $sut = new CsvFileStorage(__DIR__ . "/../__fakes__/object-with-pkey.csv", FakeValueObjectWithPrimaryKey::class); $primaryKey = "123"; $itemKey = new ItemKey($primaryKey); $item = $sut->getByKey($itemKey); @@ -176,7 +172,7 @@ public function testab() #[TestDox("Shall load more than one object from file")] public function testb() { - $sut = new CsvFileStorage("tests/__fakes__/object-2.csv", FakeValueObject::class); + $sut = new CsvFileStorage(__DIR__ . "/../__fakes__/object-2.csv", FakeValueObject::class); $this->assertCount(2, $sut); } @@ -184,7 +180,7 @@ public function testb() public function testc() { $this->expectException(DomainException::class); - new CsvFileStorage("tests/__fakes__/object-malformed.csv", FakeValueObject::class); + new CsvFileStorage(__DIR__ . "/../__fakes__/object-malformed.csv", FakeValueObject::class); } #[TestDox("Shall not set first line when file is empty")] @@ -197,7 +193,7 @@ public function testd() #[TestDox("Shall parse into target object having union types")] public function teste() { - $sut = new CsvFileStorage("tests/__fakes__/object-unions.csv", FakeValueObjectWithUnions::class); + $sut = new CsvFileStorage(__DIR__ . "/../__fakes__/object-unions.csv", FakeValueObjectWithUnions::class); $this->assertContainsOnlyInstancesOf(FakeValueObjectWithUnions::class, $sut->getAll()); } @@ -205,7 +201,7 @@ public function teste() public function testf() { $this->expectException(AmbiguousUnionTypeException::class); - new CsvFileStorage("tests/__fakes__/object-unions-malformed.csv", FakeValueObjectWithUnionsError::class); + new CsvFileStorage(__DIR__ . "/../__fakes__/object-unions-malformed.csv", FakeValueObjectWithUnionsError::class); } #[TestDox("Shall throw an exception when an invalid value is commited")]