From aa45786c5de730d48d7b27bbc005d997a7b953b0 Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Wed, 7 May 2025 08:59:01 +0200 Subject: [PATCH 1/8] Split table mapper and queries --- composer.json | 14 +- composer.lock | 1812 ++++++++--------- phpcs.xml | 48 +- src/Column/BoolColumn.php | 13 + src/Column/Column.php | 25 +- src/Column/IntegerColumn.php | 13 + src/Column/MultiTypeColumn.php | 9 - src/Column/Nullable/NullableBoolColumn.php | 15 + src/Column/Nullable/NullableIntegerColumn.php | 15 + src/Column/Nullable/NullableNumericColumn.php | 15 + src/Column/Nullable/NullableStringColumn.php | 15 + src/Column/NumericColumn.php | 13 + src/Column/StringColumn.php | 13 + src/Column/TableColumn.php | 18 - src/Connection/Connection.php | 6 +- src/Connection/ConnectionDriver.php | 11 + src/Connection/Pdo/GenericConnection.php | 2 +- src/Connection/Pdo/MysqlConnection.php | 6 + .../Pdo/NestedTransactionConnection.php | 4 +- src/EntityRecordMapper.php | 26 + src/EntityRecordRepository.php | 58 + src/EntityTable.php | 61 - src/RecordMapper.php | 48 + src/RecordRepository.php | 169 ++ src/Table.php | 204 -- src/TableColumn.php | 65 + src/TableColumnNullable.php | 65 + tests/Fixtures/Address.php | 62 +- tests/Fixtures/AddressEntityRecordMapper.php | 37 + tests/Fixtures/AddressEntityTable.php | 34 - tests/Fixtures/AddressRecordMapper.php | 38 + tests/Fixtures/AddressTable.php | 38 - tests/TestCase.php | 28 + .../Connection/Pdo/GenericConnectionTest.php | 51 +- .../Connection/Pdo/MysqlConnectionTest.php | 35 +- .../Pdo/NestedTransactionConnectionTest.php | 80 +- tests/Unit/EntityManagerTest.php | 106 + .../EntityTableMapperDeleteTest.php} | 10 +- .../EntityTableMapperLoadByIdTest.php} | 10 +- .../EntityTableMapperUpdateTest.php} | 12 +- .../TableMapperDeleteWhereTest.php} | 10 +- .../TableMapperExistsTest.php} | 10 +- .../TableMapperInsertTest.php} | 10 +- .../TableMapperLoadByQueryTest.php} | 10 +- .../TableMapperLoadByTest.php} | 10 +- .../TableMapperSelectByQueryTest.php} | 10 +- .../TableMapperSelectTest.php} | 10 +- tests/Unit/Old/TableMapperTest.php | 35 + tests/Unit/{ => Old}/TableUpdateWhereTest.php | 8 +- tests/Unit/RecordManagerTest.php | 238 +++ tests/Unit/TableTest.php | 32 - 51 files changed, 2171 insertions(+), 1516 deletions(-) create mode 100644 src/Column/BoolColumn.php create mode 100644 src/Column/IntegerColumn.php delete mode 100644 src/Column/MultiTypeColumn.php create mode 100644 src/Column/Nullable/NullableBoolColumn.php create mode 100644 src/Column/Nullable/NullableIntegerColumn.php create mode 100644 src/Column/Nullable/NullableNumericColumn.php create mode 100644 src/Column/Nullable/NullableStringColumn.php create mode 100644 src/Column/NumericColumn.php create mode 100644 src/Column/StringColumn.php delete mode 100644 src/Column/TableColumn.php create mode 100644 src/Connection/ConnectionDriver.php create mode 100644 src/EntityRecordMapper.php create mode 100644 src/EntityRecordRepository.php delete mode 100644 src/EntityTable.php create mode 100644 src/RecordMapper.php create mode 100644 src/RecordRepository.php delete mode 100644 src/Table.php create mode 100644 src/TableColumn.php create mode 100644 src/TableColumnNullable.php create mode 100644 tests/Fixtures/AddressEntityRecordMapper.php delete mode 100644 tests/Fixtures/AddressEntityTable.php create mode 100644 tests/Fixtures/AddressRecordMapper.php delete mode 100644 tests/Fixtures/AddressTable.php create mode 100644 tests/TestCase.php create mode 100644 tests/Unit/EntityManagerTest.php rename tests/Unit/{EntityTableDeleteTest.php => Old/EntityTableMapperDeleteTest.php} (74%) rename tests/Unit/{EntityTableLoadByIdTest.php => Old/EntityTableMapperLoadByIdTest.php} (80%) rename tests/Unit/{EntityTableUpdateTest.php => Old/EntityTableMapperUpdateTest.php} (71%) rename tests/Unit/{TableDeleteWhereTest.php => Old/TableMapperDeleteWhereTest.php} (74%) rename tests/Unit/{TableExistsTest.php => Old/TableMapperExistsTest.php} (86%) rename tests/Unit/{TableInsertTest.php => Old/TableMapperInsertTest.php} (77%) rename tests/Unit/{TableLoadByQueryTest.php => Old/TableMapperLoadByQueryTest.php} (82%) rename tests/Unit/{TableLoadByTest.php => Old/TableMapperLoadByTest.php} (83%) rename tests/Unit/{TableSelectByQueryTest.php => Old/TableMapperSelectByQueryTest.php} (86%) rename tests/Unit/{TableSelectTest.php => Old/TableMapperSelectTest.php} (92%) create mode 100644 tests/Unit/Old/TableMapperTest.php rename tests/Unit/{ => Old}/TableUpdateWhereTest.php (81%) create mode 100644 tests/Unit/RecordManagerTest.php delete mode 100644 tests/Unit/TableTest.php diff --git a/composer.json b/composer.json index 2d816fc..5598b22 100644 --- a/composer.json +++ b/composer.json @@ -10,16 +10,16 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.4", "ext-json": "*", "ext-pdo": "*" }, "require-dev": { - "infection/infection": "^0.26.19", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.5", - "slevomat/coding-standard": "^8.9", - "symfony/var-dumper": "^5.2" + "infection/infection": "^0.29", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^12.1", + "slevomat/coding-standard": "^8.18", + "symfony/var-dumper": "^7.2" }, "autoload": { "psr-4": { @@ -32,7 +32,7 @@ } }, "scripts": { - "cs:check": "vendor/bin/phpcs --colors -ps", + "cs:check": "vendor/bin/phpcs -s --colors --runtime-set testVersion 8.4", "cs:fix": "vendor/bin/phpcbf --colors -ps", "mutation": "vendor/bin/infection --threads=2 --min-msi=100 --ansi", "test:stan": "php -d memory_limit=-1 vendor/bin/phpstan analyse src --level=max --ansi", diff --git a/composer.lock b/composer.lock index 5ac3ada..d82b9a1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,38 +4,35 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c6e053e38468460998443a9b54ca09f6", + "content-hash": "4d9489c6eaf5f9f621bd8bef13d92d3e", "packages": [], "packages-dev": [ { "name": "colinodell/json5", - "version": "v2.3.0", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/colinodell/json5.git", - "reference": "15b063f8cb5e6deb15f0cd39123264ec0d19c710" + "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinodell/json5/zipball/15b063f8cb5e6deb15f0cd39123264ec0d19c710", - "reference": "15b063f8cb5e6deb15f0cd39123264ec0d19c710", + "url": "https://api.github.com/repos/colinodell/json5/zipball/5724d21bc5c910c2560af1b8915f0cc0163579c8", + "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "^7.1.3|^8.0" - }, - "conflict": { - "scrutinizer/ocular": "1.7.*" + "php": "^8.0" }, "require-dev": { - "mikehaertl/php-shellcommand": "^1.2.5", - "phpstan/phpstan": "^1.4", - "scrutinizer/ocular": "^1.6", - "squizlabs/php_codesniffer": "^2.3 || ^3.0", - "symfony/finder": "^4.4|^5.4|^6.0", - "symfony/phpunit-bridge": "^5.4|^6.0" + "mikehaertl/php-shellcommand": "^1.7.0", + "phpstan/phpstan": "^1.10.57", + "scrutinizer/ocular": "^1.9", + "squizlabs/php_codesniffer": "^3.8.1", + "symfony/finder": "^6.0|^7.0", + "symfony/phpunit-bridge": "^7.0.3" }, "bin": [ "bin/json5" @@ -43,7 +40,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -76,7 +73,7 @@ ], "support": { "issues": "https://github.com/colinodell/json5/issues", - "source": "https://github.com/colinodell/json5/tree/v2.3.0" + "source": "https://github.com/colinodell/json5/tree/v3.0.0" }, "funding": [ { @@ -96,32 +93,40 @@ "type": "patreon" } ], - "time": "2022-12-27T16:44:40+00:00" + "time": "2024-02-09T13:06:12+00:00" }, { "name": "composer/pcre", - "version": "3.1.0", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { "dev-main": "3.x-dev" } @@ -151,7 +156,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { @@ -167,20 +172,20 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { "name": "composer/xdebug-handler", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { @@ -191,7 +196,7 @@ "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -215,9 +220,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { @@ -233,39 +238,42 @@ "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", + "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -281,7 +289,7 @@ }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", @@ -305,93 +313,23 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, - "time": "2022-02-04T12:51:07+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "fidry/cpu-core-counter", - "version": "0.4.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2" + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/79261cc280aded96d098e1b0e0ba0c4881b432c2", - "reference": "79261cc280aded96d098e1b0e0ba0c4881b432c2", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { @@ -399,13 +337,13 @@ }, "require-dev": { "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", "phpstan/phpstan": "^1.9.2", "phpstan/phpstan-deprecation-rules": "^1.0.0", "phpstan/phpstan-phpunit": "^1.2.2", "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^9.5.26 || ^8.5.31", - "theofidry/php-cs-fixer-config": "^1.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, "type": "library", @@ -431,7 +369,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.1" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" }, "funding": [ { @@ -439,7 +377,7 @@ "type": "github" } ], - "time": "2022-12-16T22:01:02+00:00" + "time": "2024-08-06T10:04:20+00:00" }, { "name": "infection/abstract-testframework-adapter", @@ -620,63 +558,63 @@ }, { "name": "infection/infection", - "version": "0.26.19", + "version": "0.29.14", "source": { "type": "git", "url": "https://github.com/infection/infection.git", - "reference": "bd7351c88f3a797ea8977e68fe6a3f4d4c5f457f" + "reference": "feea2a48a8aeedd3a4d2105167b41a46f0e568a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/bd7351c88f3a797ea8977e68fe6a3f4d4c5f457f", - "reference": "bd7351c88f3a797ea8977e68fe6a3f4d4c5f457f", + "url": "https://api.github.com/repos/infection/infection/zipball/feea2a48a8aeedd3a4d2105167b41a46f0e568a3", + "reference": "feea2a48a8aeedd3a4d2105167b41a46f0e568a3", "shasum": "" }, "require": { - "colinodell/json5": "^2.2", + "colinodell/json5": "^2.2 || ^3.0", "composer-runtime-api": "^2.0", "composer/xdebug-handler": "^2.0 || ^3.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "fidry/cpu-core-counter": "^0.4.0", + "fidry/cpu-core-counter": "^0.4.0 || ^0.5.0 || ^1.0", "infection/abstract-testframework-adapter": "^0.5.0", "infection/extension-installer": "^0.1.0", "infection/include-interceptor": "^0.2.5", - "justinrainbow/json-schema": "^5.2.10", - "nikic/php-parser": "^4.15.1", + "infection/mutator": "^0.4", + "justinrainbow/json-schema": "^5.3 || ^6.0", + "nikic/php-parser": "^5.3", "ondram/ci-detector": "^4.1.0", - "php": "^8.0", + "php": "^8.2", "sanmai/later": "^0.1.1", "sanmai/pipeline": "^5.1 || ^6", - "sebastian/diff": "^3.0.2 || ^4.0 || ^5.0", - "symfony/console": "^5.4 || ^6.0", - "symfony/filesystem": "^5.4 || ^6.0", - "symfony/finder": "^5.4 || ^6.0", - "symfony/process": "^5.4 || ^6.0", - "thecodingmachine/safe": "^2.1.2", + "sebastian/diff": "^3.0.2 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "symfony/console": "^6.4 || ^7.0", + "symfony/filesystem": "^6.4 || ^7.0", + "symfony/finder": "^6.4 || ^7.0", + "symfony/process": "^6.4 || ^7.0", + "thecodingmachine/safe": "^v3.0", "webmozart/assert": "^1.11" }, "conflict": { + "antecedent/patchwork": "<2.1.25", "dg/bypass-finals": "<1.4.1", "phpunit/php-code-coverage": ">9,<9.1.4 || >9.2.17,<9.2.21" }, "require-dev": { - "brianium/paratest": "^6.3", "ext-simplexml": "*", - "fidry/makefile": "^0.2.0", - "helmich/phpunit-json-assert": "^3.0", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "^1.3.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpstan/phpstan-webmozart-assert": "^1.0.2", - "phpunit/phpunit": "^9.5.5", - "symfony/phpunit-bridge": "^5.4 || ^6.0", - "symfony/yaml": "^5.4 || ^6.0", - "thecodingmachine/phpstan-safe-rule": "^1.2.0" + "fidry/makefile": "^1.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + "phpunit/phpunit": "^11.5", + "rector/rector": "^2.0", + "sidz/phpstan-rules": "^0.5.1", + "symfony/yaml": "^6.4 || ^7.0", + "thecodingmachine/phpstan-safe-rule": "^1.4" }, "bin": [ "bin/infection" @@ -732,7 +670,60 @@ ], "support": { "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.26.19" + "source": "https://github.com/infection/infection/tree/0.29.14" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2025-03-02T18:49:12+00:00" + }, + { + "name": "infection/mutator", + "version": "0.4.1", + "source": { + "type": "git", + "url": "https://github.com/infection/mutator.git", + "reference": "3c976d721b02b32f851ee4e15d553ef1e9186d1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/mutator/zipball/3c976d721b02b32f851ee4e15d553ef1e9186d1d", + "reference": "3c976d721b02b32f851ee4e15d553ef1e9186d1d", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\Mutator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Mutator interface to implement custom mutators (mutation operators) for Infection", + "support": { + "issues": "https://github.com/infection/mutator/issues", + "source": "https://github.com/infection/mutator/tree/0.4.1" }, "funding": [ { @@ -744,29 +735,34 @@ "type": "open_collective" } ], - "time": "2023-02-05T21:47:26+00:00" + "time": "2025-04-29T08:19:52+00:00" }, { "name": "justinrainbow/json-schema", - "version": "5.2.12", + "version": "6.4.1", "source": { "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + "url": "https://github.com/jsonrainbow/json-schema.git", + "reference": "35d262c94959571e8736db1e5c9bc36ab94ae900" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", - "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/35d262c94959571e8736db1e5c9bc36ab94ae900", + "reference": "35d262c94959571e8736db1e5c9bc36ab94ae900", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-json": "*", + "marc-mabe/php-enum": "^4.0", + "php": "^7.2 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "friendsofphp/php-cs-fixer": "3.3.0", "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" + "marc-mabe/php-enum-phpstan": "^2.0", + "phpspec/prophecy": "^1.19", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^8.5" }, "bin": [ "bin/validate-json" @@ -774,7 +770,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "6.x-dev" } }, "autoload": { @@ -805,29 +801,102 @@ } ], "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", + "homepage": "https://github.com/jsonrainbow/json-schema", "keywords": [ "json", "schema" ], "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + "issues": "https://github.com/jsonrainbow/json-schema/issues", + "source": "https://github.com/jsonrainbow/json-schema/tree/6.4.1" }, - "time": "2022-04-13T08:02:27+00:00" + "time": "2025-04-04T13:08:07+00:00" + }, + { + "name": "marc-mabe/php-enum", + "version": "v4.7.1", + "source": { + "type": "git", + "url": "https://github.com/marc-mabe/php-enum.git", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "reference": "7159809e5cfa041dca28e61f7f7ae58063aae8ed", + "shasum": "" + }, + "require": { + "ext-reflection": "*", + "php": "^7.1 | ^8.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.16.10 || ^1.0.4", + "phpstan/phpstan": "^1.3.1", + "phpunit/phpunit": "^7.5.20 | ^8.5.22 | ^9.5.11", + "vimeo/psalm": "^4.17.0 | ^5.26.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.2-dev", + "dev-master": "4.7-dev" + } + }, + "autoload": { + "psr-4": { + "MabeEnum\\": "src/" + }, + "classmap": [ + "stubs/Stringable.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Marc Bennewitz", + "email": "dev@mabe.berlin", + "homepage": "https://mabe.berlin/", + "role": "Lead" + } + ], + "description": "Simple and fast implementation of enumerations with native PHP", + "homepage": "https://github.com/marc-mabe/php-enum", + "keywords": [ + "enum", + "enum-map", + "enum-set", + "enumeration", + "enumerator", + "enummap", + "enumset", + "map", + "set", + "type", + "type-hint", + "typehint" + ], + "support": { + "issues": "https://github.com/marc-mabe/php-enum/issues", + "source": "https://github.com/marc-mabe/php-enum/tree/v4.7.1" + }, + "time": "2024-11-28T04:54:44+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { @@ -835,11 +904,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -865,7 +935,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" }, "funding": [ { @@ -873,29 +943,31 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -903,7 +975,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -927,35 +999,35 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "ondram/ci-detector", - "version": "4.1.0", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/OndraM/ci-detector.git", - "reference": "8a4b664e916df82ff26a44709942dfd593fa6f30" + "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/8a4b664e916df82ff26a44709942dfd593fa6f30", - "reference": "8a4b664e916df82ff26a44709942dfd593fa6f30", + "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", + "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { - "ergebnis/composer-normalize": "^2.2", - "lmc/coding-standard": "^1.3 || ^2.1", + "ergebnis/composer-normalize": "^2.13.2", + "lmc/coding-standard": "^3.0.0", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0.5", - "phpstan/phpstan": "^0.12.58", - "phpstan/phpstan-phpunit": "^0.12.16", - "phpunit/phpunit": "^7.1 || ^8.0 || ^9.0" + "phpstan/extension-installer": "^1.1.0", + "phpstan/phpstan": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.0.0", + "phpunit/phpunit": "^9.6.13" }, "type": "library", "autoload": { @@ -1005,26 +1077,27 @@ ], "support": { "issues": "https://github.com/OndraM/ci-detector/issues", - "source": "https://github.com/OndraM/ci-detector/tree/4.1.0" + "source": "https://github.com/OndraM/ci-detector/tree/4.2.0" }, - "time": "2021-04-14T09:16:52+00:00" + "time": "2024-03-12T13:22:30+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -1065,9 +1138,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -1122,28 +1201,30 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.16.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "symfony/process": "^5.2" }, "type": "library", @@ -1161,26 +1242,26 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" }, - "time": "2023-02-07T18:11:17+00:00" + "time": "2025-02-19T13:28:12+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.9", + "version": "2.1.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "9b13dafe3d66693d20fe5729c3dde1d31bb64703" + "reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9b13dafe3d66693d20fe5729c3dde1d31bb64703", - "reference": "9b13dafe3d66693d20fe5729c3dde1d31bb64703", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8f2e03099cac24ff3b379864d171c5acbfc6b9a2", + "reference": "8f2e03099cac24ff3b379864d171c5acbfc6b9a2", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -1219,45 +1300,40 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2023-03-30T08:58:01+00:00" + "time": "2025-05-02T15:32:28+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.26", + "version": "12.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" + "reference": "448f2c504d86dbff3949dcd02c95aa85db2c7617" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", - "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/448f2c504d86dbff3949dcd02c95aa85db2c7617", + "reference": "448f2c504d86dbff3949dcd02c95aa85db2c7617", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "nikic/php-parser": "^5.4.0", + "php": ">=8.3", + "phpunit/php-file-iterator": "^6.0", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.1" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -1266,7 +1342,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "12.2.x-dev" } }, "autoload": { @@ -1294,40 +1370,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2023-03-06T12:58:08+00:00" + "time": "2025-05-04T05:25:05+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "961bc913d42fe24a257bfff826a5068079ac7782" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782", + "reference": "961bc913d42fe24a257bfff826a5068079ac7782", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1354,7 +1443,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.0" }, "funding": [ { @@ -1362,28 +1452,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2025-02-07T04:58:37+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-pcntl": "*" @@ -1391,7 +1481,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1417,7 +1507,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" }, "funding": [ { @@ -1425,32 +1516,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2025-02-07T04:58:58+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1476,7 +1567,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" }, "funding": [ { @@ -1484,32 +1576,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2025-02-07T04:59:16+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -1535,7 +1627,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" }, "funding": [ { @@ -1543,54 +1636,48 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2025-02-07T04:59:38+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.6", + "version": "12.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115" + "reference": "5ee57ad690bda2c487594577600931a99053436c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5ee57ad690bda2c487594577600931a99053436c", + "reference": "5ee57ad690bda2c487594577600931a99053436c", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.1.2", + "phpunit/php-file-iterator": "^6.0.0", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.0.0", + "sebastian/comparator": "^7.0.1", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.0.0", + "sebastian/exporter": "^7.0.0", + "sebastian/global-state": "^8.0.0", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/type": "^6.0.2", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -1598,7 +1685,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "12.1-dev" } }, "autoload": { @@ -1630,7 +1717,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.1.4" }, "funding": [ { @@ -1641,12 +1728,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2023-03-27T11:43:46+00:00" + "time": "2025-05-02T07:01:56+00:00" }, { "name": "psr/container", @@ -1703,16 +1798,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1747,37 +1842,43 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sanmai/later", - "version": "0.1.2", + "version": "0.1.5", "source": { "type": "git", "url": "https://github.com/sanmai/later.git", - "reference": "9b659fecef2030193fd02402955bc39629d5606f" + "reference": "cf5164557d19930295892094996f049ea12ba14d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/later/zipball/9b659fecef2030193fd02402955bc39629d5606f", - "reference": "9b659fecef2030193fd02402955bc39629d5606f", + "url": "https://api.github.com/repos/sanmai/later/zipball/cf5164557d19930295892094996f049ea12ba14d", + "reference": "cf5164557d19930295892094996f049ea12ba14d", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.13", - "infection/infection": ">=0.10.5", + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3.35.1", + "infection/infection": ">=0.27.6", "phan/phan": ">=2", "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": ">=0.10", - "phpunit/phpunit": ">=7.4", + "phpstan/phpstan": ">=1.4.5", + "phpunit/phpunit": ">=9.5 <10", "vimeo/psalm": ">=2" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, "autoload": { "files": [ "src/functions.php" @@ -1799,7 +1900,7 @@ "description": "Later: deferred wrapper object", "support": { "issues": "https://github.com/sanmai/later/issues", - "source": "https://github.com/sanmai/later/tree/0.1.2" + "source": "https://github.com/sanmai/later/tree/0.1.5" }, "funding": [ { @@ -1807,34 +1908,34 @@ "type": "github" } ], - "time": "2021-01-02T10:26:44+00:00" + "time": "2024-12-06T02:36:26+00:00" }, { "name": "sanmai/pipeline", - "version": "v6.3", + "version": "6.12", "source": { "type": "git", "url": "https://github.com/sanmai/pipeline.git", - "reference": "929b115ca58d62b6b2574702df1ebde4562c7c43" + "reference": "ad7dbc3f773eeafb90d5459522fbd8f188532e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/929b115ca58d62b6b2574702df1ebde4562c7c43", - "reference": "929b115ca58d62b6b2574702df1ebde4562c7c43", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/ad7dbc3f773eeafb90d5459522fbd8f188532e25", + "reference": "ad7dbc3f773eeafb90d5459522fbd8f188532e25", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.8", - "friendsofphp/php-cs-fixer": "^3", + "friendsofphp/php-cs-fixer": "^3.17", "infection/infection": ">=0.10.5", - "league/pipeline": "^1.0 || ^0.3", + "league/pipeline": "^0.3 || ^1.0", "phan/phan": ">=1.1", "php-coveralls/php-coveralls": "^2.4.1", "phpstan/phpstan": ">=0.10", - "phpunit/phpunit": "^7.4 || ^8.1 || ^9.4", + "phpunit/phpunit": ">=9.4", "vimeo/psalm": ">=2" }, "type": "library", @@ -1864,7 +1965,7 @@ "description": "General-purpose collections pipeline", "support": { "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/v6.3" + "source": "https://github.com/sanmai/pipeline/tree/6.12" }, "funding": [ { @@ -1872,32 +1973,32 @@ "type": "github" } ], - "time": "2022-11-30T06:07:06+00:00" + "time": "2024-10-17T02:22:57+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c", + "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1920,7 +2021,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.0.0" }, "funding": [ { @@ -1928,145 +2030,39 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2025-02-07T04:53:50+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b478f34614f934e0291598d0c08cbaba9644bee5", + "reference": "b478f34614f934e0291598d0c08cbaba9644bee5", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2105,7 +2101,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.0.1" }, "funding": [ { @@ -2113,33 +2110,33 @@ "type": "github" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-03-07T07:00:32+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2162,7 +2159,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" }, "funding": [ { @@ -2170,33 +2168,33 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2025-02-07T04:55:25+00:00" }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2228,7 +2226,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" }, "funding": [ { @@ -2236,27 +2235,27 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2025-02-07T04:55:46+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8afe311eca49171bf95405cc0078be9a3821f9f2", + "reference": "8afe311eca49171bf95405cc0078be9a3821f9f2", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "suggest": { "ext-posix": "*" @@ -2264,7 +2263,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2283,7 +2282,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -2291,7 +2290,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.0" }, "funding": [ { @@ -2299,34 +2299,34 @@ "type": "github" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2025-02-07T04:56:08+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "76432aafc58d50691a00d86d0632f1217a47b688" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688", + "reference": "76432aafc58d50691a00d86d0632f1217a47b688", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2368,7 +2368,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.0" }, "funding": [ { @@ -2376,38 +2377,35 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2025-02-07T04:56:42+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.5", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", - "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc", + "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -2426,13 +2424,14 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.0" }, "funding": [ { @@ -2440,33 +2439,33 @@ "type": "github" } ], - "time": "2022-02-14T08:28:10+00:00" + "time": "2025-02-07T04:56:59+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -2489,7 +2488,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" }, "funding": [ { @@ -2497,34 +2497,34 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2025-02-07T04:57:28+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2546,7 +2546,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" }, "funding": [ { @@ -2554,32 +2555,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2025-02-07T04:57:48+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2601,7 +2602,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" }, "funding": [ { @@ -2609,32 +2611,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2025-02-07T04:58:17+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c", + "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -2664,7 +2666,8 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0" }, "funding": [ { @@ -2672,87 +2675,32 @@ "type": "github" } ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2025-02-07T05:00:01+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069", + "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^12.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2775,7 +2723,8 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.2" }, "funding": [ { @@ -2783,29 +2732,29 @@ "type": "github" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2025-03-18T13:37:31+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -2828,7 +2777,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" }, "funding": [ { @@ -2836,36 +2786,36 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2025-02-07T05:00:38+00:00" }, { "name": "slevomat/coding-standard", - "version": "8.9.1", + "version": "8.18.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "3d4fe0c803ae15829ef72d90d3d4eee3dd9f79b2" + "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/3d4fe0c803ae15829ef72d90d3d4eee3dd9f79b2", - "reference": "3d4fe0c803ae15829ef72d90d3d4eee3dd9f79b2", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/f3b23cb9b26301b8c3c7bb03035a1bee23974593", + "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": ">=1.16.0 <1.17.0", - "squizlabs/php_codesniffer": "^3.7.1" + "php": "^7.4 || ^8.0", + "phpstan/phpdoc-parser": "^2.1.0", + "squizlabs/php_codesniffer": "^3.12.2" }, "require-dev": { - "phing/phing": "2.17.4", - "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.10.8", - "phpstan/phpstan-deprecation-rules": "1.1.3", - "phpstan/phpstan-phpunit": "1.0.0|1.3.10", - "phpstan/phpstan-strict-rules": "1.5.0", - "phpunit/phpunit": "7.5.20|8.5.21|9.6.5" + "phing/phing": "3.0.1", + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/phpstan": "2.1.13", + "phpstan/phpstan-deprecation-rules": "2.0.2", + "phpstan/phpstan-phpunit": "2.0.6", + "phpstan/phpstan-strict-rules": "2.0.4", + "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.17|12.1.3" }, "type": "phpcodesniffer-standard", "extra": { @@ -2889,7 +2839,7 @@ ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.9.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.18.0" }, "funding": [ { @@ -2901,20 +2851,20 @@ "type": "tidelift" } ], - "time": "2023-03-27T11:00:16+00:00" + "time": "2025-05-01T09:40:50+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.2", + "version": "3.12.2", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", - "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", + "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", "shasum": "" }, "require": { @@ -2924,11 +2874,11 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", "extra": { @@ -2943,68 +2893,144 @@ "authors": [ { "name": "Greg Sherwood", - "role": "lead" + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", "keywords": [ "phpcs", "standards", "static analysis" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-04-13T04:10:18+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, - "time": "2023-02-22T23:07:41+00:00" + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/console", - "version": "v6.2.8", + "version": "v7.2.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" + "reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", + "url": "https://api.github.com/repos/symfony/console/zipball/0e2e3f38c192e93e622e41ec37f4ca70cfedf218", + "reference": "0e2e3f38c192e93e622e41ec37f4ca70cfedf218", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3038,7 +3064,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.8" + "source": "https://github.com/symfony/console/tree/v7.2.6" }, "funding": [ { @@ -3054,20 +3080,20 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2025-04-07T19:09:28+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -3075,12 +3101,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -3105,7 +3131,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -3121,27 +3147,30 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -3168,7 +3197,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" }, "funding": [ { @@ -3184,27 +3213,27 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/finder", - "version": "v6.2.7", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3232,7 +3261,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.7" + "source": "https://github.com/symfony/finder/tree/v7.2.2" }, "funding": [ { @@ -3248,24 +3277,24 @@ "type": "tidelift" } ], - "time": "2023-02-16T09:57:23+00:00" + "time": "2024-12-30T19:00:17+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -3275,12 +3304,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3314,7 +3340,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -3330,36 +3356,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3395,7 +3418,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -3411,36 +3434,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3479,7 +3499,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -3495,24 +3515,25 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -3522,12 +3543,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3562,7 +3580,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -3578,107 +3596,24 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/process", - "version": "v6.2.8", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", + "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -3706,7 +3641,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.8" + "source": "https://github.com/symfony/process/tree/v7.2.5" }, "funding": [ { @@ -3722,40 +3657,38 @@ "type": "tidelift" } ], - "time": "2023-03-09T16:20:02+00:00" + "time": "2025-03-13T12:21:46+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -3791,7 +3724,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -3807,38 +3740,39 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v6.2.8", + "version": "v7.2.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "url": "https://api.github.com/repos/symfony/string/zipball/a214fe7d62bd4df2a76447c67c6b26e1d5e74931", + "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3877,7 +3811,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.8" + "source": "https://github.com/symfony/string/tree/v7.2.6" }, "funding": [ { @@ -3893,42 +3827,36 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2025-04-20T20:18:16+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.22", + "version": "v7.2.6", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4" + "reference": "9c46038cd4ed68952166cf7001b54eb539184ccb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4", - "reference": "e2edac9ce47e6df07e38143c7cfa6bdbc1a6dcc4", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9c46038cd4ed68952166cf7001b54eb539184ccb", + "reference": "9c46038cd4ed68952166cf7001b54eb539184ccb", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" + "symfony/console": "<6.4" }, "require-dev": { "ext-iconv": "*", - "symfony/console": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/uid": "^5.1|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -3966,7 +3894,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.22" + "source": "https://github.com/symfony/var-dumper/tree/v7.2.6" }, "funding": [ { @@ -3982,50 +3910,35 @@ "type": "tidelift" } ], - "time": "2023-03-25T09:27:28+00:00" + "time": "2025-04-09T08:14:01+00:00" }, { "name": "thecodingmachine/safe", - "version": "v2.4.0", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/thecodingmachine/safe.git", - "reference": "e788f3d09dcd36f806350aedb77eac348fafadd3" + "reference": "234f6fe34a0bead8c5ae1cfc0800539442e6f619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/e788f3d09dcd36f806350aedb77eac348fafadd3", - "reference": "e788f3d09dcd36f806350aedb77eac348fafadd3", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/234f6fe34a0bead8c5ae1cfc0800539442e6f619", + "reference": "234f6fe34a0bead8c5ae1cfc0800539442e6f619", "shasum": "" }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { - "phpstan/phpstan": "^1.5", - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.2", - "thecodingmachine/phpstan-strict-rules": "^1.0" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.x-dev" - } - }, "autoload": { "files": [ - "deprecated/apc.php", - "deprecated/array.php", - "deprecated/datetime.php", - "deprecated/libevent.php", - "deprecated/misc.php", - "deprecated/password.php", - "deprecated/mssql.php", - "deprecated/stats.php", - "deprecated/strings.php", "lib/special_cases.php", - "deprecated/mysqli.php", "generated/apache.php", "generated/apcu.php", "generated/array.php", @@ -4065,6 +3978,7 @@ "generated/mbstring.php", "generated/misc.php", "generated/mysql.php", + "generated/mysqli.php", "generated/network.php", "generated/oci8.php", "generated/opcache.php", @@ -4077,6 +3991,7 @@ "generated/ps.php", "generated/pspell.php", "generated/readline.php", + "generated/rnp.php", "generated/rpminfo.php", "generated/rrd.php", "generated/sem.php", @@ -4108,7 +4023,6 @@ "lib/DateTime.php", "lib/DateTimeImmutable.php", "lib/Exceptions/", - "deprecated/Exceptions/", "generated/Exceptions/" ] }, @@ -4119,22 +4033,36 @@ "description": "PHP core functions that throw exceptions instead of returning FALSE on error", "support": { "issues": "https://github.com/thecodingmachine/safe/issues", - "source": "https://github.com/thecodingmachine/safe/tree/v2.4.0" + "source": "https://github.com/thecodingmachine/safe/tree/v3.1.1" }, - "time": "2022-10-07T14:02:17+00:00" + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2025-04-28T07:56:17+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -4163,7 +4091,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -4171,7 +4099,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2024-03-03T12:36:25+00:00" }, { "name": "webmozart/assert", @@ -4234,14 +4162,14 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.1", + "php": ">=8.4", "ext-json": "*", "ext-pdo": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/phpcs.xml b/phpcs.xml index d16fc82..a1521e6 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -5,7 +5,6 @@ - @@ -14,7 +13,6 @@ - @@ -29,9 +27,9 @@ + - @@ -40,55 +38,35 @@ - - - - + - - - - - - - - - - - + + + + error - + + tests/* - + tests/* - - - - - - - - - + + tests/* - - - - + + tests/* diff --git a/src/Column/BoolColumn.php b/src/Column/BoolColumn.php new file mode 100644 index 0000000..edff561 --- /dev/null +++ b/src/Column/BoolColumn.php @@ -0,0 +1,13 @@ + + */ +readonly class BoolColumn extends Column +{ +} diff --git a/src/Column/Column.php b/src/Column/Column.php index 6dfa6b1..d3ed1af 100644 --- a/src/Column/Column.php +++ b/src/Column/Column.php @@ -6,14 +6,27 @@ use Closure; -abstract class Column +/** + * @template Entry of object + * @template Type + */ +abstract readonly class Column { - public readonly string $name; - public readonly Closure $value; + /** + * @param Closure(Entry $record): Type $value + */ + public function __construct( + public string $name, + public Closure $value, + ) { + } - public function __construct(string $name, Closure $value) + /** + * @param Entry $entry + * @return Type + */ + public function valueOn($entry) { - $this->name = $name; - $this->value = $value; + return ($this->value)($entry); } } diff --git a/src/Column/IntegerColumn.php b/src/Column/IntegerColumn.php new file mode 100644 index 0000000..f0b23fe --- /dev/null +++ b/src/Column/IntegerColumn.php @@ -0,0 +1,13 @@ + + */ +readonly class IntegerColumn extends Column +{ +} diff --git a/src/Column/MultiTypeColumn.php b/src/Column/MultiTypeColumn.php deleted file mode 100644 index f25555d..0000000 --- a/src/Column/MultiTypeColumn.php +++ /dev/null @@ -1,9 +0,0 @@ - + */ +readonly class NullableBoolColumn extends Column +{ +} diff --git a/src/Column/Nullable/NullableIntegerColumn.php b/src/Column/Nullable/NullableIntegerColumn.php new file mode 100644 index 0000000..903bb60 --- /dev/null +++ b/src/Column/Nullable/NullableIntegerColumn.php @@ -0,0 +1,15 @@ + + */ +readonly class NullableIntegerColumn extends Column +{ +} diff --git a/src/Column/Nullable/NullableNumericColumn.php b/src/Column/Nullable/NullableNumericColumn.php new file mode 100644 index 0000000..c46b9ae --- /dev/null +++ b/src/Column/Nullable/NullableNumericColumn.php @@ -0,0 +1,15 @@ + + */ +readonly class NullableNumericColumn extends Column +{ +} diff --git a/src/Column/Nullable/NullableStringColumn.php b/src/Column/Nullable/NullableStringColumn.php new file mode 100644 index 0000000..b77e0a9 --- /dev/null +++ b/src/Column/Nullable/NullableStringColumn.php @@ -0,0 +1,15 @@ + + */ +readonly class NullableStringColumn extends Column +{ +} diff --git a/src/Column/NumericColumn.php b/src/Column/NumericColumn.php new file mode 100644 index 0000000..46b2aff --- /dev/null +++ b/src/Column/NumericColumn.php @@ -0,0 +1,13 @@ + + */ +readonly class NumericColumn extends Column +{ +} diff --git a/src/Column/StringColumn.php b/src/Column/StringColumn.php new file mode 100644 index 0000000..57174d8 --- /dev/null +++ b/src/Column/StringColumn.php @@ -0,0 +1,13 @@ + + */ +readonly class StringColumn extends Column +{ +} diff --git a/src/Column/TableColumn.php b/src/Column/TableColumn.php deleted file mode 100644 index 15a3497..0000000 --- a/src/Column/TableColumn.php +++ /dev/null @@ -1,18 +0,0 @@ -columns[] = $column; - - return $column; - } -} diff --git a/src/Connection/Connection.php b/src/Connection/Connection.php index b4dc1e0..76f683f 100644 --- a/src/Connection/Connection.php +++ b/src/Connection/Connection.php @@ -8,6 +8,8 @@ interface Connection { + public function driver(): ConnectionDriver; + public function begin(): void; public function commit(): void; @@ -17,12 +19,12 @@ public function rollback(): void; public function transaction(callable $fn): mixed; /** - * @param array $params + * @param array $params */ public function read(string $statement, array $params = []): PDOStatement; /** - * @param array $params + * @param array $params */ public function write(string $statement, array $params = []): PDOStatement; } diff --git a/src/Connection/ConnectionDriver.php b/src/Connection/ConnectionDriver.php new file mode 100644 index 0000000..e8748b3 --- /dev/null +++ b/src/Connection/ConnectionDriver.php @@ -0,0 +1,11 @@ + + */ +abstract class EntityRecordMapper extends RecordMapper +{ + /** @var Column */ + public readonly Column $primaryKey; + + /** + * @param Column $primaryKey + */ + public function __construct(Column $primaryKey) + { + $this->primaryKey = $primaryKey; + } +} diff --git a/src/EntityRecordRepository.php b/src/EntityRecordRepository.php new file mode 100644 index 0000000..ee8865e --- /dev/null +++ b/src/EntityRecordRepository.php @@ -0,0 +1,58 @@ + + * @internal + */ +abstract class EntityRecordRepository extends RecordRepository +{ + /** + * @param EntityRecordMapper $entityMapper + */ + public function __construct( + Connection $connection, + protected EntityRecordMapper $entityMapper, + ) { + parent::__construct($connection, $entityMapper); + } + + /** + * @param FK $id + * @return T|null + */ + public function loadById($id) + { + return $this->loadBy(['id' => $id]); + } + + /** + * @param T ...$entities + */ + public function update(...$entities): void + { + foreach ($entities as $entity) { + $this->updateWhere( + $this->mapper->values($entity), + ['id' => $this->entityMapper->primaryKey->valueOn($entity)], + ); + } + } + + /** + * @param T ...$entities + */ + public function delete(...$entities): void + { + foreach ($entities as $entity) { + $this->deleteWhere(['id' => $this->entityMapper->primaryKey->valueOn($entity)]); + } + } +} diff --git a/src/EntityTable.php b/src/EntityTable.php deleted file mode 100644 index 0291114..0000000 --- a/src/EntityTable.php +++ /dev/null @@ -1,61 +0,0 @@ - - */ -abstract class EntityTable extends Table -{ - private Column $primaryKey; - - public function __construct(Column $primaryKey, Connection $connection) - { - $this->primaryKey = $primaryKey; - - parent::__construct($connection); - } - - /** - * @return T|null - * @throws Throwable - */ - public function loadById(int|string $id): ?object - { - return $this->loadBy(['id' => $id]); - } - - /** - * @param T ...$entities - */ - public function update(object ...$entities): void - { - foreach ($entities as $entity) { - $this->updateWhere( - array_reduce( - $this->columns, - fn($current, Column $column) => array_merge($current, [$column->name => ($column->value)($entity)]), - [], - ), - ['id' => ($this->primaryKey->value)($entity)], - ); - } - } - - /** - * @param T ...$entities - */ - public function delete(object ...$entities): void - { - foreach ($entities as $entity) { - $this->deleteWhere(['id' => ($this->primaryKey->value)($entity)]); - } - } -} diff --git a/src/RecordMapper.php b/src/RecordMapper.php new file mode 100644 index 0000000..ee21fd9 --- /dev/null +++ b/src/RecordMapper.php @@ -0,0 +1,48 @@ + + */ +abstract class RecordMapper +{ + /** @use TableColumn */ + use TableColumn; + + /** @use TableColumnNullable */ + use TableColumnNullable; + + /** @var list> */ + public private(set) array $columns = []; + + /** + * @param array $row + * @return T + */ + abstract public function entry(array $row); + + /** + * @param T $entry + * @return array + */ + public function values($entry): array + { + $entries = array_map( + fn(Column $column) => [$column->name => $column->valueOn($entry)], + $this->columns, + ); + + return array_merge(...$entries); + } + + public function names(): string + { + return join(', ', array_map(fn(Column $column) => $column->name, $this->columns)); + } +} diff --git a/src/RecordRepository.php b/src/RecordRepository.php new file mode 100644 index 0000000..563a9e5 --- /dev/null +++ b/src/RecordRepository.php @@ -0,0 +1,169 @@ +table ({$this->mapper->names()}) VALUES ({$this->bindings()})"; + + $values = $this->mapper->values($entry); + + $this->connection->write($sql, $values); + } + + /** + * @param array $where + * @return T|null + */ + public function loadBy(array $where) + { + [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); + $sql = trim("SELECT * FROM $this->table$whereColumnsString LIMIT 1"); + $items = $this->connection->read($sql, $whereBindings); + + /** @var array $item */ + $item = $items->fetch(PDO::FETCH_ASSOC); + + return $item ? $this->mapper->entry($item) : null; + } + + /** + * @param array $bindings + * @return T|null + */ + public function loadByQuery(string $selectQuery, array $bindings) + { + $items = $this->connection->read($selectQuery, $bindings); + + /** @var array $item */ + $item = $items->fetch(PDO::FETCH_ASSOC); + + return $item ? $this->mapper->entry($item) : null; + } + + /** + * @param array $where + * @return Traversable + */ + public function listBy(array $where = [], ?int $limit = null, ?int $offset = null): Traversable + { + [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); + $limitOffset = $this->prepareLimitOffset($limit, $offset); + $sql = trim("SELECT * FROM $this->table$whereColumnsString$limitOffset"); + $items = $this->connection->read($sql, $whereBindings); + + while ($item = $items->fetch(PDO::FETCH_ASSOC)) { + /** @var array $item */ + yield $this->mapper->entry($item); + } + } + + /** + * @param array $bindings + * @return Traversable + */ + public function listByQuery(string $selectQuery, array $bindings): Traversable + { + $items = $this->connection->read($selectQuery, $bindings); + + while ($item = $items->fetch(PDO::FETCH_ASSOC)) { + /** @var array $item */ + yield $this->mapper->entry($item); + } + } + + /** + * @param array $where + */ + public function exists(array $where): bool + { + return $this->loadBy($where) !== null; + } + + /** + * @param array $where + */ + public function deleteWhere(array $where): void + { + [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); + $sql = trim("DELETE FROM $this->table$whereColumnsString"); + + $this->connection->write($sql, $whereBindings); + } + + /** + * @param array $values + * @param array $where + */ + public function updateWhere(array $values, array $where): void + { + $columnBindings = []; + $valuesBinding = []; + + foreach ($values as $column => $value) { + $columnBindings[] = "$column = :$column"; + $valuesBinding[$column] = $value; + } + + $columnBindingsString = join(", ", $columnBindings); + + [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); + $sql = trim("UPDATE $this->table SET $columnBindingsString$whereColumnsString"); + + $this->connection->write($sql, array_merge($valuesBinding, $whereBindings)); + } + + private function bindings(): string + { + return join(', ', array_map(fn(Column $column) => ":$column->name", $this->mapper->columns)); + } + + /** + * @param array $where + * @return array{0: string, 1: array} + */ + private function prepareWhere(array $where): array + { + $whereColumns = []; + $whereBindings = []; + + foreach ($where as $column => $value) { + $whereColumns[] = "$column = :$column"; + $whereBindings[$column] = $value; + } + + return [ + empty($whereColumns) ? '' : sprintf(" WHERE %s", join(' AND ', $whereColumns)), + $whereBindings, + ]; + } + + private function prepareLimitOffset(?int $limit, ?int $offset): string + { + return ($limit ? " LIMIT $limit" : '') . ($offset ? " OFFSET $offset" : ''); + } +} diff --git a/src/Table.php b/src/Table.php deleted file mode 100644 index e343590..0000000 --- a/src/Table.php +++ /dev/null @@ -1,204 +0,0 @@ - */ - protected array $columns = []; - protected Connection $connection; - - abstract public function name(): string; - - /** - * @param array $row - * @return T - */ - abstract public function entry(array $row); - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * @param T $entry - */ - public function insert(object $entry): void - { - $sql = "INSERT INTO {$this->name()} ({$this->columnNames()}) VALUES ({$this->columnBindings()})"; - - $values = $this->valuesOf($entry); - - $this->connection->write($sql, $values); - } - - /** - * @param array $where - * @return T|null - * @throws Throwable - */ - public function loadBy(array $where): ?object - { - [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); - $sql = trim("SELECT * FROM {$this->name()}$whereColumnsString LIMIT 1"); - $items = $this->connection->read($sql, $whereBindings); - - /** @var array $item */ - $item = $items->fetch(PDO::FETCH_ASSOC); - - return $item ? $this->entry($item) : null; - } - - /** - * @param array $bindings - * @return T|null - * @throws Throwable - */ - public function loadByQuery(string $selectQuery, array $bindings): ?object - { - $items = $this->connection->read($selectQuery, $bindings); - - /** @var array $item */ - $item = $items->fetch(PDO::FETCH_ASSOC); - - return $item ? $this->entry($item) : null; - } - - /** - * @param array $where - * @return Traversable - */ - public function findBy(array $where, ?int $limit = null, ?int $offset = null): Traversable - { - [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); - $limitOffset = $this->prepareLimitOffset($limit, $offset); - $sql = trim("SELECT * FROM {$this->name()}{$whereColumnsString}{$limitOffset}"); - $items = $this->connection->read($sql, $whereBindings); - - while ($item = $items->fetch(PDO::FETCH_ASSOC)) { - /** @var array $item */ - yield $this->entry($item); - } - } - - /** - * @param array $bindings - * @return Traversable - */ - public function findByQuery(string $selectQuery, array $bindings): Traversable - { - $items = $this->connection->read($selectQuery, $bindings); - - while ($item = $items->fetch(PDO::FETCH_ASSOC)) { - /** @var array $item */ - yield $this->entry($item); - } - } - - /** - * @param array $where - */ - public function deleteWhere(array $where): void - { - [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); - $sql = trim("DELETE FROM {$this->name()}$whereColumnsString"); - - $this->connection->write($sql, $whereBindings); - } - - /** - * @param array $where - * @throws Throwable - */ - public function exists(array $where): bool - { - return $this->loadBy($where) !== null; - } - - /** - * @param array $values - * @param array $where - */ - public function updateWhere(array $values, array $where): void - { - $columnBindings = []; - $valuesBinding = []; - - foreach ($values as $column => $value) { - $key = ":$column"; - $columnBindings[] = "$column = $key"; - $valuesBinding[$key] = $value; - } - - $columnBindingsString = join(", ", $columnBindings); - - [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); - $sql = trim("UPDATE {$this->name()} SET {$columnBindingsString}{$whereColumnsString}"); - - $this->connection->write($sql, array_merge($valuesBinding, $whereBindings)); - } - - /** - * @param T $entry - * @return array - */ - public function valuesOf(object $entry): array - { - return array_reduce( - $this->columns, - fn($current, Column $column) => array_merge($current, ["$column->name" => ($column->value)($entry)]), - [], - ); - } - - private function columnNames(): string - { - return join(', ', array_map(fn(Column $column) => $column->name, $this->columns)); - } - - private function columnBindings(): string - { - return join(', ', array_map(fn(Column $column) => ":$column->name", $this->columns)); - } - - /** - * @param array $where - * @return array{0: string, 1: array} - */ - private function prepareWhere(array $where): array - { - $whereColumns = []; - $whereBindings = []; - - foreach ($where as $column => $value) { - $binding = ":$column"; - $whereColumns[] = "$column = $binding"; - $whereBindings[$binding] = $value; - } - - return [ - empty($whereColumns) ? '' : sprintf(" WHERE %s", join(' AND ', $whereColumns)), - $whereBindings, - ]; - } - - private function prepareLimitOffset(?int $limit, ?int $offset): string - { - return ($limit ? " LIMIT $limit" : '') . ($offset ? " OFFSET $offset" : ''); - } -} diff --git a/src/TableColumn.php b/src/TableColumn.php new file mode 100644 index 0000000..76bef92 --- /dev/null +++ b/src/TableColumn.php @@ -0,0 +1,65 @@ + + */ + protected function string(string $name, Closure $value): StringColumn + { + $column = new StringColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): bool $value + * @return BoolColumn + */ + protected function boolean(string $name, Closure $value): BoolColumn + { + $column = new BoolColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): numeric $value + * @return NumericColumn + */ + protected function numeric(string $name, Closure $value): NumericColumn + { + $column = new NumericColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): int $value + * @return IntegerColumn + */ + protected function integer(string $name, Closure $value): IntegerColumn + { + $column = new IntegerColumn($name, $value); + $this->columns[] = $column; + + return $column; + } +} diff --git a/src/TableColumnNullable.php b/src/TableColumnNullable.php new file mode 100644 index 0000000..7221c6b --- /dev/null +++ b/src/TableColumnNullable.php @@ -0,0 +1,65 @@ + + */ + protected function nullableString(string $name, Closure $value): NullableStringColumn + { + $column = new NullableStringColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): bool $value + * @return NullableBoolColumn + */ + protected function nullableBoolean(string $name, Closure $value): NullableBoolColumn + { + $column = new NullableBoolColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): numeric $value + * @return NullableNumericColumn + */ + protected function nullableNumeric(string $name, Closure $value): NullableNumericColumn + { + $column = new NullableNumericColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): int $value + * @return NullableIntegerColumn + */ + protected function nullableInteger(string $name, Closure $value): NullableIntegerColumn + { + $column = new NullableIntegerColumn($name, $value); + $this->columns[] = $column; + + return $column; + } +} diff --git a/tests/Fixtures/Address.php b/tests/Fixtures/Address.php index a2d68b1..c6c267d 100644 --- a/tests/Fixtures/Address.php +++ b/tests/Fixtures/Address.php @@ -4,14 +4,64 @@ namespace Test\Tcds\Io\Orm\Fixtures; -class Address +readonly class Address { - public readonly string $id; - public readonly string $street; + public function __construct( + public string $id, + public string $street, + public float $number, + public int $floor, + public bool $active, + ) { + } + + public static function first(): self + { + return new self( + id: 'address-1', + street: 'First Avenue', + number: 145.45, + floor: 1, + active: true, + ); + } + + public static function second(): self + { + return new self( + id: 'address-2', + street: 'Second Avenue', + number: 34.90, + floor: 5, + active: false, + ); + } + + /** + * @return array + */ + public static function firstRowData(): array + { + return [ + 'id' => 'address-1', + 'street' => 'First Avenue', + 'number' => 145.45, + 'floor' => 1, + 'active' => true, + ]; + } - public function __construct(string $id, string $street) + /** + * @return array + */ + public static function secondRowData(): array { - $this->id = $id; - $this->street = $street; + return [ + 'id' => 'address-2', + 'street' => 'Second Avenue', + 'number' => 34.90, + 'floor' => 5, + 'active' => false, + ]; } } diff --git a/tests/Fixtures/AddressEntityRecordMapper.php b/tests/Fixtures/AddressEntityRecordMapper.php new file mode 100644 index 0000000..0eb8e75 --- /dev/null +++ b/tests/Fixtures/AddressEntityRecordMapper.php @@ -0,0 +1,37 @@ + + */ +final class AddressEntityRecordMapper extends EntityRecordMapper +{ + public function __construct() + { + parent::__construct($this->string('id', fn(Address $entity) => $entity->id)); + + $this->string('street', fn(Address $entity) => $entity->street); + $this->numeric('number', fn(Address $entity) => $entity->number); + $this->integer('floor', fn(Address $entity) => $entity->floor); + $this->boolean('active', fn(Address $entity) => $entity->active); + } + + /** + * @param array $row + */ + public function entry(array $row): Address + { + return new Address( + id: $row['id'], + street: $row['street'], + number: $row['number'], + floor: $row['floor'], + active: $row['active'], + ); + } +} diff --git a/tests/Fixtures/AddressEntityTable.php b/tests/Fixtures/AddressEntityTable.php deleted file mode 100644 index 25a9a6a..0000000 --- a/tests/Fixtures/AddressEntityTable.php +++ /dev/null @@ -1,34 +0,0 @@ -column('street', fn(Address $entity) => $entity->street); - - parent::__construct($this->column('id', fn(Address $entity) => $entity->id), $connection); - } - - public function name(): string - { - return "addresses"; - } - - /** - * @param array $row - */ - public function entry(array $row): Address - { - return new Address( - id: $row['id'], - street: $row['street'], - ); - } -} diff --git a/tests/Fixtures/AddressRecordMapper.php b/tests/Fixtures/AddressRecordMapper.php new file mode 100644 index 0000000..aa235e1 --- /dev/null +++ b/tests/Fixtures/AddressRecordMapper.php @@ -0,0 +1,38 @@ +string("id", fn(Address $entity) => $entity->id); + $this->string("street", fn(Address $entity) => $entity->street); + } + + public function table(): string + { + return "addresses"; + } + + /** + * @param array $row + */ + public function entry(array $row): Address + { + return new Address( + id: $row['id'], + street: $row['street'], + number: $row['number'], + floor: $row['floor'], + active: $row['active'], + ); + } +} diff --git a/tests/Fixtures/AddressTable.php b/tests/Fixtures/AddressTable.php deleted file mode 100644 index 88e3a7f..0000000 --- a/tests/Fixtures/AddressTable.php +++ /dev/null @@ -1,38 +0,0 @@ -column("id", fn(Address $entity) => $entity->id); - $this->column("street", fn(Address $entity) => $entity->street); - - parent::__construct($connection); - } - - public function name(): string - { - return "addresses"; - } - - /** - * @param array $row - */ - public function entry(array $row): Address - { - return new Address( - id: $row['id'], - street: $row['street'], - ); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..2cc0a73 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,28 @@ + ...$expectedCalls + */ + public function consecutive(InvokedCountMatcher $matcher, array ...$expectedCalls): Callback + { + $expectedCalls = array_values($expectedCalls); + + return $this->callback(function (...$args) use ($matcher, $expectedCalls) { + $index = $matcher->numberOfInvocations() - 1; + + $this->assertEquals($expectedCalls[$index], $args); + + return true; + }); + } +} diff --git a/tests/Unit/Connection/Pdo/GenericConnectionTest.php b/tests/Unit/Connection/Pdo/GenericConnectionTest.php index cfa3b51..2cbe987 100644 --- a/tests/Unit/Connection/Pdo/GenericConnectionTest.php +++ b/tests/Unit/Connection/Pdo/GenericConnectionTest.php @@ -8,8 +8,9 @@ use PDO; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; +use Tcds\Io\Orm\Connection\ConnectionDriver; use Tcds\Io\Orm\Connection\Pdo\GenericConnection; +use Test\Tcds\Io\Orm\TestCase; class GenericConnectionTest extends TestCase { @@ -25,27 +26,27 @@ protected function setUp(): void $this->write = $this->createMock(PDO::class); $this->statement = $this->createMock(PDOStatement::class); - $this->connection = new GenericConnection($this->read, $this->write); + $this->connection = new class ($this->read, $this->write) extends GenericConnection + { + public function driver(): ConnectionDriver + { + return ConnectionDriver::GENERIC; + } + }; } public function testGivenPdoThenConfigurePdo(): void { - $this->read - ->expects($this->exactly(2)) - ->method('setAttribute') - ->withConsecutive( - [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], - [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], - ); - $this->write - ->expects($this->exactly(2)) - ->method('setAttribute') - ->withConsecutive( - [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], - [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], - ); - - new GenericConnection($this->read, $this->write); + $this->expectToSetupPdo($this->read); + $this->expectToSetupPdo($this->write); + + new class ($this->read, $this->write) extends GenericConnection + { + public function driver(): ConnectionDriver + { + return ConnectionDriver::GENERIC; + } + }; } public function testGivenTheQueryAndItsParamsWhenExecuteIsCalledThenRunPrepareAndExecuteInPdo(): void @@ -132,4 +133,18 @@ public function testWhenTransactionSucceedThenCommitAndReturnCallbackResponse(): $this->assertEquals("success", $response); } + + private function expectToSetupPdo(PDO&MockObject $pdo): void + { + $matcher = $this->exactly(2); + + $pdo + ->expects($matcher) + ->method('setAttribute') + ->with($this->consecutive( + matcher: $matcher, + first: [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], + second: [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], + )); + } } diff --git a/tests/Unit/Connection/Pdo/MysqlConnectionTest.php b/tests/Unit/Connection/Pdo/MysqlConnectionTest.php index 82d25b5..12ecf9c 100644 --- a/tests/Unit/Connection/Pdo/MysqlConnectionTest.php +++ b/tests/Unit/Connection/Pdo/MysqlConnectionTest.php @@ -5,8 +5,9 @@ namespace Test\Tcds\Io\Orm\Unit\Connection\Pdo; use PDO; -use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; use Tcds\Io\Orm\Connection\Pdo\MysqlConnection; +use Test\Tcds\Io\Orm\TestCase; class MysqlConnectionTest extends TestCase { @@ -15,22 +16,24 @@ public function testGivenPdoThenConfigurePdoWithMysqlInitCommand(): void $read = $this->createMock(PDO::class); $write = $this->createMock(PDO::class); - $read->expects($this->exactly(3)) - ->method('setAttribute') - ->withConsecutive( - [PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES utf8'], - [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], - [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], - ); - - $write->expects($this->exactly(3)) - ->method('setAttribute') - ->withConsecutive( - [PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES utf8'], - [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], - [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], - ); + $this->expectToSetupPdo($read); + $this->expectToSetupPdo($write); new MysqlConnection($read, $write); } + + private function expectToSetupPdo(PDO&MockObject $pdo): void + { + $matcher = $this->exactly(3); + + $pdo + ->expects($matcher) + ->method('setAttribute') + ->with($this->consecutive( + matcher: $matcher, + first: [PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES utf8'], + second: [PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION], + third: [PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC], + )); + } } diff --git a/tests/Unit/Connection/Pdo/NestedTransactionConnectionTest.php b/tests/Unit/Connection/Pdo/NestedTransactionConnectionTest.php index 1db04a2..7a26653 100644 --- a/tests/Unit/Connection/Pdo/NestedTransactionConnectionTest.php +++ b/tests/Unit/Connection/Pdo/NestedTransactionConnectionTest.php @@ -7,8 +7,9 @@ use PDO; use PDOException; use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; +use Tcds\Io\Orm\Connection\ConnectionDriver; use Tcds\Io\Orm\Connection\Pdo\NestedTransactionConnection; +use Test\Tcds\Io\Orm\TestCase; class NestedTransactionConnectionTest extends TestCase { @@ -21,16 +22,22 @@ protected function setUp(): void $read = $this->createMock(PDO::class); $this->write = $this->createMock(PDO::class); - $this->connection = new NestedTransactionConnection($read, $this->write); + $this->connection = new class ($read, $this->write) extends NestedTransactionConnection + { + public function driver(): ConnectionDriver + { + return ConnectionDriver::GENERIC; + } + }; } public function testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements(): void { - $this->write->expects($this->exactly(2))->method('exec') - ->withConsecutive( - ['BEGIN'], - ['COMMIT'], - ); + $this->expectToSetupPdo( + $this->write, + ['BEGIN'], + ['COMMIT'], + ); $this->connection->begin(); $this->connection->commit(); @@ -38,15 +45,15 @@ public function testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements public function testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint(): void { - $this->write->expects($this->exactly(6))->method('exec') - ->withConsecutive( - ['BEGIN'], - ['SAVEPOINT LEVEL1'], - ['SAVEPOINT LEVEL2'], - ['RELEASE SAVEPOINT LEVEL2'], - ['RELEASE SAVEPOINT LEVEL1'], - ['COMMIT'], - ); + $this->expectToSetupPdo( + $this->write, + ['BEGIN'], + ['SAVEPOINT LEVEL1'], + ['SAVEPOINT LEVEL2'], + ['RELEASE SAVEPOINT LEVEL2'], + ['RELEASE SAVEPOINT LEVEL1'], + ['COMMIT'], + ); $this->connection->begin(); $this->connection->begin(); @@ -58,11 +65,11 @@ public function testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndRele public function testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatements(): void { - $this->write->expects($this->exactly(2))->method('exec') - ->withConsecutive( - ['BEGIN'], - ['ROLLBACK'], - ); + $this->expectToSetupPdo( + $this->write, + ['BEGIN'], + ['ROLLBACK'], + ); $this->connection->begin(); $this->connection->rollback(); @@ -70,15 +77,15 @@ public function testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatem public function testWhenBeginAndRollbackGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint(): void { - $this->write->expects($this->exactly(6))->method('exec') - ->withConsecutive( - ['BEGIN'], - ['SAVEPOINT LEVEL1'], - ['SAVEPOINT LEVEL2'], - ['RELEASE SAVEPOINT LEVEL2'], - ['RELEASE SAVEPOINT LEVEL1'], - ['ROLLBACK'], - ); + $this->expectToSetupPdo( + $this->write, + ['BEGIN'], + ['SAVEPOINT LEVEL1'], + ['SAVEPOINT LEVEL2'], + ['RELEASE SAVEPOINT LEVEL2'], + ['RELEASE SAVEPOINT LEVEL1'], + ['ROLLBACK'], + ); $this->connection->begin(); $this->connection->begin(); @@ -95,4 +102,17 @@ public function testGivenNoTransactionThenWhenRollbackIsCalledThenThrowAnExcepti $this->connection->rollback(); } + + /** + * @param array ...$params + */ + private function expectToSetupPdo(PDO&MockObject $pdo, array ...$params): void + { + $matcher = $this->exactly(count($params)); + + $pdo + ->expects($matcher) + ->method('exec') + ->with($this->consecutive($matcher, ...$params)); + } } diff --git a/tests/Unit/EntityManagerTest.php b/tests/Unit/EntityManagerTest.php new file mode 100644 index 0000000..4449dcb --- /dev/null +++ b/tests/Unit/EntityManagerTest.php @@ -0,0 +1,106 @@ +connection = $this->createMock(Connection::class); + + $this->manager = new class (new AddressEntityRecordMapper(), $this->connection) extends EntityRecordRepository + { + protected string $table { + get { + return 'entity_table'; + } + } + }; + } + + #[Test] public function load_by_id(): void + { + $this->connection + ->expects($this->once()) + ->method('read') + ->with('SELECT * FROM entity_table WHERE id = :id LIMIT 1', ['id' => 'galaxy-1']); + + $this->manager->loadById('galaxy-1'); + } + + #[Test] public function update(): void + { + $matcher = $this->exactly(2); + + $this->connection + ->expects($matcher) + ->method('write') + ->with($this->consecutive( + $matcher, + [ + 'UPDATE entity_table SET id = :id, street = :street, number = :number, floor = :floor, active = :active WHERE id = :id', + [ + 'id' => 'address-1', + 'street' => 'First Avenue', + 'number' => 145.45, + 'floor' => 1, + 'active' => true, + ], + ], + [ + 'UPDATE entity_table SET id = :id, street = :street, number = :number, floor = :floor, active = :active WHERE id = :id', + [ + 'id' => 'address-2', + 'street' => 'Second Avenue', + 'number' => 34.9, + 'floor' => 5, + 'active' => false, + ], + ], + )); + + $this->manager->update( + Address::first(), + Address::second(), + ); + } + + #[Test] public function delete(): void + { + $matcher = $this->exactly(2); + + $this->connection + ->expects($matcher) + ->method('write') + ->with($this->consecutive( + $matcher, + [ + 'DELETE FROM entity_table WHERE id = :id', + ['id' => 'address-1'], + ], + [ + 'DELETE FROM entity_table WHERE id = :id', + ['id' => 'address-2'], + ], + )); + + $this->manager->delete( + Address::first(), + Address::second(), + ); + } +} diff --git a/tests/Unit/EntityTableDeleteTest.php b/tests/Unit/Old/EntityTableMapperDeleteTest.php similarity index 74% rename from tests/Unit/EntityTableDeleteTest.php rename to tests/Unit/Old/EntityTableMapperDeleteTest.php index ae370f5..0498bb5 100644 --- a/tests/Unit/EntityTableDeleteTest.php +++ b/tests/Unit/Old/EntityTableMapperDeleteTest.php @@ -2,23 +2,23 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressEntityTable; +use Test\Tcds\Io\Orm\Fixtures\AddressEntityRecordMapper; -class EntityTableDeleteTest extends TestCase +class EntityTableMapperDeleteTest extends TestCase { private Connection&MockObject $connection; - private AddressEntityTable $table; + private AddressEntityRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); - $this->table = new AddressEntityTable($this->connection); + $this->table = new AddressEntityRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void diff --git a/tests/Unit/EntityTableLoadByIdTest.php b/tests/Unit/Old/EntityTableMapperLoadByIdTest.php similarity index 80% rename from tests/Unit/EntityTableLoadByIdTest.php rename to tests/Unit/Old/EntityTableMapperLoadByIdTest.php index b7f414b..43fcbce 100644 --- a/tests/Unit/EntityTableLoadByIdTest.php +++ b/tests/Unit/Old/EntityTableMapperLoadByIdTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressEntityTable; +use Test\Tcds\Io\Orm\Fixtures\AddressEntityRecordMapper; -class EntityTableLoadByIdTest extends TestCase +class EntityTableMapperLoadByIdTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressEntityTable $table; + private AddressEntityRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressEntityTable($this->connection); + $this->table = new AddressEntityRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void diff --git a/tests/Unit/EntityTableUpdateTest.php b/tests/Unit/Old/EntityTableMapperUpdateTest.php similarity index 71% rename from tests/Unit/EntityTableUpdateTest.php rename to tests/Unit/Old/EntityTableMapperUpdateTest.php index 2d6056a..0a7337c 100644 --- a/tests/Unit/EntityTableUpdateTest.php +++ b/tests/Unit/Old/EntityTableMapperUpdateTest.php @@ -2,23 +2,23 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressEntityTable; +use Test\Tcds\Io\Orm\Fixtures\AddressEntityRecordMapper; -class EntityTableUpdateTest extends TestCase +class EntityTableMapperUpdateTest extends TestCase { private Connection&MockObject $connection; - private AddressEntityTable $table; + private AddressEntityRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); - $this->table = new AddressEntityTable($this->connection); + $this->table = new AddressEntityRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void @@ -29,7 +29,7 @@ public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturn ->expects($this->once()) ->method('write') ->with( - "UPDATE addresses SET street = :street, id = :id WHERE id = :id", + "UPDATE addresses SET id = :id, street = :street WHERE id = :id", [':street' => 'Galaxy Avenue', ':id' => 'address-xxx'], ); diff --git a/tests/Unit/TableDeleteWhereTest.php b/tests/Unit/Old/TableMapperDeleteWhereTest.php similarity index 74% rename from tests/Unit/TableDeleteWhereTest.php rename to tests/Unit/Old/TableMapperDeleteWhereTest.php index b95aa50..ec62025 100644 --- a/tests/Unit/TableDeleteWhereTest.php +++ b/tests/Unit/Old/TableMapperDeleteWhereTest.php @@ -2,22 +2,22 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableDeleteWhereTest extends TestCase +class TableMapperDeleteWhereTest extends TestCase { private Connection&MockObject $connection; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void diff --git a/tests/Unit/TableExistsTest.php b/tests/Unit/Old/TableMapperExistsTest.php similarity index 86% rename from tests/Unit/TableExistsTest.php rename to tests/Unit/Old/TableMapperExistsTest.php index 1a4151d..9f28dd8 100644 --- a/tests/Unit/TableExistsTest.php +++ b/tests/Unit/Old/TableMapperExistsTest.php @@ -2,25 +2,25 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableExistsTest extends TestCase +class TableMapperExistsTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenTheConditionsWhenSelectReturnsAnEntryThenExistIsTrue(): void diff --git a/tests/Unit/TableInsertTest.php b/tests/Unit/Old/TableMapperInsertTest.php similarity index 77% rename from tests/Unit/TableInsertTest.php rename to tests/Unit/Old/TableMapperInsertTest.php index 0652e1c..57b9591 100644 --- a/tests/Unit/TableInsertTest.php +++ b/tests/Unit/Old/TableMapperInsertTest.php @@ -2,23 +2,23 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableInsertTest extends TestCase +class TableMapperInsertTest extends TestCase { private Connection&MockObject $connection; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenAnEntryThenRunInsertWithItsData(): void diff --git a/tests/Unit/TableLoadByQueryTest.php b/tests/Unit/Old/TableMapperLoadByQueryTest.php similarity index 82% rename from tests/Unit/TableLoadByQueryTest.php rename to tests/Unit/Old/TableMapperLoadByQueryTest.php index e1a3142..690ec15 100644 --- a/tests/Unit/TableLoadByQueryTest.php +++ b/tests/Unit/Old/TableMapperLoadByQueryTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableLoadByQueryTest extends TestCase +class TableMapperLoadByQueryTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenASqlQueryAndItsBindingsThenBypassTheQueryToTheConnectionAndReturnTheFirstItem(): void diff --git a/tests/Unit/TableLoadByTest.php b/tests/Unit/Old/TableMapperLoadByTest.php similarity index 83% rename from tests/Unit/TableLoadByTest.php rename to tests/Unit/Old/TableMapperLoadByTest.php index d3f75d4..afe896e 100644 --- a/tests/Unit/TableLoadByTest.php +++ b/tests/Unit/Old/TableMapperLoadByTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableLoadByTest extends TestCase +class TableMapperLoadByTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void diff --git a/tests/Unit/TableSelectByQueryTest.php b/tests/Unit/Old/TableMapperSelectByQueryTest.php similarity index 86% rename from tests/Unit/TableSelectByQueryTest.php rename to tests/Unit/Old/TableMapperSelectByQueryTest.php index 699ebee..6c12c2f 100644 --- a/tests/Unit/TableSelectByQueryTest.php +++ b/tests/Unit/Old/TableMapperSelectByQueryTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableSelectByQueryTest extends TestCase +class TableMapperSelectByQueryTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenASqlQueryAndItsBindingsThenBypassTheQueryToTheConnectionAndReturnTheItemList(): void diff --git a/tests/Unit/TableSelectTest.php b/tests/Unit/Old/TableMapperSelectTest.php similarity index 92% rename from tests/Unit/TableSelectTest.php rename to tests/Unit/Old/TableMapperSelectTest.php index c1acae2..9f2ea2b 100644 --- a/tests/Unit/TableSelectTest.php +++ b/tests/Unit/Old/TableMapperSelectTest.php @@ -2,26 +2,26 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PDOStatement; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; -class TableSelectTest extends TestCase +class TableMapperSelectTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenWhereConditionWhenNotEmptyThenSelectWithWhereStatement(): void diff --git a/tests/Unit/Old/TableMapperTest.php b/tests/Unit/Old/TableMapperTest.php new file mode 100644 index 0000000..ce78148 --- /dev/null +++ b/tests/Unit/Old/TableMapperTest.php @@ -0,0 +1,35 @@ +createMock(Connection::class); + + $this->table = new AddressRecordMapper($connection); + } + + public function testGetEntryValues(): void + { + $address = new Address(id: 'address-xxx', street: 'Galaxy Avenue'); + + $values = $this->table->values($address); + + $this->assertEquals([ + 'id' => 'address-xxx', + 'street' => 'Galaxy Avenue', + ], $values); + } +} diff --git a/tests/Unit/TableUpdateWhereTest.php b/tests/Unit/Old/TableUpdateWhereTest.php similarity index 81% rename from tests/Unit/TableUpdateWhereTest.php rename to tests/Unit/Old/TableUpdateWhereTest.php index 6ed02bc..f420322 100644 --- a/tests/Unit/TableUpdateWhereTest.php +++ b/tests/Unit/Old/TableUpdateWhereTest.php @@ -2,22 +2,22 @@ declare(strict_types=1); -namespace Test\Tcds\Io\Orm\Unit; +namespace Test\Tcds\Io\Orm\Unit\Old; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Tcds\Io\Orm\Connection\Connection; -use Test\Tcds\Io\Orm\Fixtures\AddressTable; +use Test\Tcds\Io\Orm\Fixtures\AddressRecordMapper; class TableUpdateWhereTest extends TestCase { private Connection&MockObject $connection; - private AddressTable $table; + private AddressRecordMapper $table; protected function setUp(): void { $this->connection = $this->createMock(Connection::class); - $this->table = new AddressTable($this->connection); + $this->table = new AddressRecordMapper($this->connection); } public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void diff --git a/tests/Unit/RecordManagerTest.php b/tests/Unit/RecordManagerTest.php new file mode 100644 index 0000000..b49b20f --- /dev/null +++ b/tests/Unit/RecordManagerTest.php @@ -0,0 +1,238 @@ +connection = $this->createMock(Connection::class); + $this->statement = $this->createMock(PDOStatement::class); + + $this->manager = new class (new AddressEntityRecordMapper(), $this->connection) extends RecordRepository + { + protected string $table { + get { + return 'record_table'; + } + } + }; + } + + #[Test] public function insert(): void + { + $address = Address::first(); + + $this->connection + ->expects($this->once()) + ->method('write') + ->with( + "INSERT INTO addresses (id, street, number, floor, active) VALUES (:id, :street, :number, :floor, :active)", + [ + 'id' => 'address-1', + 'street' => 'First Avenue', + 'number' => 145.45, + 'floor' => 1, + 'active' => true, + ], + ); + + $this->manager->insert($address); + } + + #[Test] public function load_by(): void + { + $this->statement + ->method('fetch') + ->willReturn(Address::secondRowData()); + $this->connection + ->expects($this->once()) + ->method('read') + ->with( + "SELECT * FROM addresses WHERE id = :id LIMIT 1", + ['id' => 'address-xxx'], + ) + ->willReturn($this->statement); + + $result = $this->manager->loadBy(['id' => 'address-xxx']); + + $this->assertEquals(Address::second(), $result); + } + + #[Test] public function load_by_query(): void + { + $this->statement + ->method('fetch') + ->willReturn(Address::firstRowData()); + $this->connection + ->expects($this->once()) + ->method('read') + ->with("select * from addresses where id LIKE :id", [':id' => 'address-xxx']) + ->willReturn($this->statement); + + $result = $this->manager->loadByQuery("select * from addresses where id LIKE :id", [':id' => 'address-xxx']); + + $this->assertEquals(Address::first(), $result); + } + + #[Test] public function list_by(): void + { + $this->statement + ->method('fetch') + ->willReturnOnConsecutiveCalls( + Address::firstRowData(), + Address::secondRowData(), + null, + ); + + $this->connection + ->expects($this->once()) + ->method('read') + ->with( + 'SELECT * FROM addresses WHERE id = :id AND street = :street LIMIT 5 OFFSET 15', + ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], + ) + ->willReturn($this->statement); + + $result = $this->manager->listBy( + ['id' => 'address-xxx', 'street' => 'Galaxy Avenue'], + limit: 5, + offset: 15, + ); + + $this->assertEquals( + [ + Address::first(), + Address::second(), + ], + iterator_to_array($result), + ); + } + + public function list_by_returns_multiple_entries(): void + { + $this->statement + ->method('fetch') + ->willReturnOnConsecutiveCalls( + ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], + ['id' => 'address-yyy', 'street' => "Galaxy Highway"], + null, + ); + + $this->connection + ->expects($this->once()) + ->method('read') + ->with("SELECT * FROM addresses", []) + ->willReturn($this->statement); + + $result = $this->manager->listBy(); + + $this->assertEquals( + [ + new Address(id: 'address-xxx', street: "Galaxy Avenue"), + new Address(id: 'address-yyy', street: "Galaxy Highway"), + ], + iterator_to_array($result), + ); + } + + #[Test] public function list_by_query(): void + { + $this->statement + ->method('fetch') + ->willReturnOnConsecutiveCalls( + Address::firstRowData(), + Address::secondRowData(), + null, + ); + + $this->connection + ->expects($this->once()) + ->method('read') + ->with("SELECT * FROM addresses WHERE foo = :foo", ['foo' => 'bar']) + ->willReturn($this->statement); + + $result = $this->manager->listByQuery('SELECT * FROM addresses WHERE foo = :foo', ['foo' => 'bar']); + + $this->assertEquals( + [ + Address::first(), + Address::second(), + ], + iterator_to_array($result), + ); + } + + #[Test] public function exists_returns_true(): void + { + $this->statement + ->method('fetch') + ->willReturn(Address::firstRowData()); + $this->connection + ->expects($this->once()) + ->method('read') + ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", ['id' => 'address-xxx']) + ->willReturn($this->statement); + + $exists = $this->manager->exists(['id' => 'address-xxx']); + + $this->assertTrue($exists); + } + + #[Test] public function exists_returns_false(): void + { + $this->statement + ->method('fetch') + ->willReturn(null); + $this->connection + ->expects($this->once()) + ->method('read') + ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", ['id' => 'address-xxx']) + ->willReturn($this->statement); + + $exists = $this->manager->exists(['id' => 'address-xxx']); + + $this->assertFalse($exists); + } + + #[Test] public function delete_where(): void + { + $this->connection + ->expects($this->once()) + ->method('write') + ->with( + "DELETE FROM addresses WHERE id = :id", + ['id' => 'address-xxx'], + ); + + $this->manager->deleteWhere(['id' => 'address-xxx']); + } + + #[Test] public function update(): void + { + $this->connection + ->expects($this->once()) + ->method('write') + ->with( + "UPDATE addresses SET street = :street WHERE id = :id", + ['street' => 'Galaxy Avenue', 'id' => 'address-xxx'], + ); + + $this->manager->updateWhere(['street' => 'Galaxy Avenue'], ['id' => 'address-xxx']); + } +} diff --git a/tests/Unit/TableTest.php b/tests/Unit/TableTest.php deleted file mode 100644 index e8ddb80..0000000 --- a/tests/Unit/TableTest.php +++ /dev/null @@ -1,32 +0,0 @@ -createMock(Connection::class); - - $this->table = new AddressTable($connection); - } - - public function testGetEntryValues(): void - { - $address = new Address(id: 'address-xxx', street: 'Galaxy Avenue'); - - $values = $this->table->valuesOf($address); - - $this->assertEquals(['id' => 'address-xxx', 'street' => 'Galaxy Avenue'], $values); - } -} From 5c7d89256fe46da82aa511e262c0c77697a6513d Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 08:27:15 +0200 Subject: [PATCH 2/8] Testing the whole project --- .phpunit.cache/test-results | 1 + composer.json | 13 +- composer.lock | 286 +++++------------- phpcs.xml | 59 +--- phpunit.xml | 31 +- src/Column/BoolColumn.php | 11 + src/Column/Column.php | 26 +- src/Column/DateColumn.php | 28 ++ src/Column/DateTimeColumn.php | 26 ++ src/Column/DateTimeImmutableColumn.php | 26 ++ src/Column/EnumColumn.php | 42 +++ .../{NumericColumn.php => FloatColumn.php} | 4 +- src/Column/Nullable/NullableBoolColumn.php | 15 - src/Column/Nullable/NullableIntegerColumn.php | 15 - src/Column/Nullable/NullableNumericColumn.php | 15 - src/Column/Nullable/NullableStringColumn.php | 15 - src/EntityRecordMapper.php | 2 +- src/EntityRecordRepository.php | 43 ++- src/RecordMapper.php | 11 +- src/RecordRepository.php | 34 +-- src/TableColumn.php | 66 +++- src/TableColumnNullable.php | 65 ---- tests/Fixtures/Address.php | 28 +- tests/Fixtures/AddressEntityRecordMapper.php | 37 --- tests/Fixtures/AddressMapper.php | 55 ++++ tests/Fixtures/AddressRecordMapper.php | 38 --- tests/Fixtures/AddressRepository.php | 26 ++ tests/Fixtures/AddressType.php | 11 + tests/Fixtures/User.php | 64 ++++ tests/Fixtures/UserMapper.php | 35 +++ tests/Fixtures/UserRepository.php | 25 ++ tests/Unit/Column/BoolColumnTest.php | 40 +++ tests/Unit/Column/ColumnTest.php | 42 +++ tests/Unit/Column/DateColumnTest.php | 48 +++ tests/Unit/Column/DateTimeColumnTest.php | 48 +++ .../Column/DateTimeImmutableColumnTest.php | 48 +++ tests/Unit/Column/EnumColumnTest.php | 47 +++ tests/Unit/Column/FloatColumnTest.php | 37 +++ tests/Unit/Column/IntegerColumnTest.php | 37 +++ tests/Unit/Column/StringColumnTest.php | 37 +++ tests/Unit/EntityManagerTest.php | 106 ------- tests/Unit/EntityRecordMapperTest.php | 75 +++++ tests/Unit/EntityRecordRepositoryTest.php | 135 +++++++++ .../Unit/Old/EntityTableMapperDeleteTest.php | 35 --- .../Old/EntityTableMapperLoadByIdTest.php | 42 --- .../Unit/Old/EntityTableMapperUpdateTest.php | 38 --- tests/Unit/Old/TableMapperDeleteWhereTest.php | 35 --- tests/Unit/Old/TableMapperExistsTest.php | 57 ---- tests/Unit/Old/TableMapperInsertTest.php | 38 --- tests/Unit/Old/TableMapperLoadByQueryTest.php | 42 --- tests/Unit/Old/TableMapperLoadByTest.php | 45 --- .../Unit/Old/TableMapperSelectByQueryTest.php | 56 ---- tests/Unit/Old/TableMapperSelectTest.php | 105 ------- tests/Unit/Old/TableMapperTest.php | 35 --- tests/Unit/Old/TableUpdateWhereTest.php | 35 --- tests/Unit/RecordMapperTest.php | 56 ++++ ...nagerTest.php => RecordRepositoryTest.php} | 42 ++- 57 files changed, 1279 insertions(+), 1235 deletions(-) create mode 100644 .phpunit.cache/test-results create mode 100644 src/Column/DateColumn.php create mode 100644 src/Column/DateTimeColumn.php create mode 100644 src/Column/DateTimeImmutableColumn.php create mode 100644 src/Column/EnumColumn.php rename src/Column/{NumericColumn.php => FloatColumn.php} (57%) delete mode 100644 src/Column/Nullable/NullableBoolColumn.php delete mode 100644 src/Column/Nullable/NullableIntegerColumn.php delete mode 100644 src/Column/Nullable/NullableNumericColumn.php delete mode 100644 src/Column/Nullable/NullableStringColumn.php delete mode 100644 src/TableColumnNullable.php delete mode 100644 tests/Fixtures/AddressEntityRecordMapper.php create mode 100644 tests/Fixtures/AddressMapper.php delete mode 100644 tests/Fixtures/AddressRecordMapper.php create mode 100644 tests/Fixtures/AddressRepository.php create mode 100644 tests/Fixtures/AddressType.php create mode 100644 tests/Fixtures/User.php create mode 100644 tests/Fixtures/UserMapper.php create mode 100644 tests/Fixtures/UserRepository.php create mode 100644 tests/Unit/Column/BoolColumnTest.php create mode 100644 tests/Unit/Column/ColumnTest.php create mode 100644 tests/Unit/Column/DateColumnTest.php create mode 100644 tests/Unit/Column/DateTimeColumnTest.php create mode 100644 tests/Unit/Column/DateTimeImmutableColumnTest.php create mode 100644 tests/Unit/Column/EnumColumnTest.php create mode 100644 tests/Unit/Column/FloatColumnTest.php create mode 100644 tests/Unit/Column/IntegerColumnTest.php create mode 100644 tests/Unit/Column/StringColumnTest.php delete mode 100644 tests/Unit/EntityManagerTest.php create mode 100644 tests/Unit/EntityRecordMapperTest.php create mode 100644 tests/Unit/EntityRecordRepositoryTest.php delete mode 100644 tests/Unit/Old/EntityTableMapperDeleteTest.php delete mode 100644 tests/Unit/Old/EntityTableMapperLoadByIdTest.php delete mode 100644 tests/Unit/Old/EntityTableMapperUpdateTest.php delete mode 100644 tests/Unit/Old/TableMapperDeleteWhereTest.php delete mode 100644 tests/Unit/Old/TableMapperExistsTest.php delete mode 100644 tests/Unit/Old/TableMapperInsertTest.php delete mode 100644 tests/Unit/Old/TableMapperLoadByQueryTest.php delete mode 100644 tests/Unit/Old/TableMapperLoadByTest.php delete mode 100644 tests/Unit/Old/TableMapperSelectByQueryTest.php delete mode 100644 tests/Unit/Old/TableMapperSelectTest.php delete mode 100644 tests/Unit/Old/TableMapperTest.php delete mode 100644 tests/Unit/Old/TableUpdateWhereTest.php create mode 100644 tests/Unit/RecordMapperTest.php rename tests/Unit/{RecordManagerTest.php => RecordRepositoryTest.php} (81%) diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results new file mode 100644 index 0000000..30d8bc5 --- /dev/null +++ b/.phpunit.cache/test-results @@ -0,0 +1 @@ +{"version":1,"defects":{"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_entry_then_get_its_plain_value":8,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_array_then_get_its_value":7,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_not_null_then_return_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_null_then_return_null":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_unset_null_then_return_null":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenPdoThenConfigurePdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenTheQueryAndItsParamsWhenExecuteIsCalledThenRunPrepareAndExecuteInPdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenStatementWhenExecIsCalledThenRunExecInPdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testBeginPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testCommitPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testRollbackPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionFailsThenRollback":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionSucceedThenCommitAndReturnCallbackResponse":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\MysqlConnectionTest::testGivenPdoThenConfigurePdoWithMysqlInitCommand":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatements":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndRollbackGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testGivenNoTransactionThenWhenRollbackIsCalledThenThrowAnException":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_first_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_second_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_first_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_second_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::load_by_id":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_first_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_second_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_first_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_second_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::insert":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by_query":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by_query":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_true":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_false":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::delete_where":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::update":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_when_value_is_null_then_get_null":8,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_one":7},"times":{"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_array_then_get_its_value":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_not_null_then_return_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_null_then_return_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_unset_null_then_return_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_array_then_get_its_value":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenPdoThenConfigurePdo":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenTheQueryAndItsParamsWhenExecuteIsCalledThenRunPrepareAndExecuteInPdo":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenStatementWhenExecIsCalledThenRunExecInPdo":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testBeginPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testCommitPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testRollbackPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionFailsThenRollback":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionSucceedThenCommitAndReturnCallbackResponse":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\MysqlConnectionTest::testGivenPdoThenConfigurePdoWithMysqlInitCommand":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatements":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndRollbackGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testGivenNoTransactionThenWhenRollbackIsCalledThenThrowAnException":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_first_user":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_second_user":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_first_user":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_second_user":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::load_by_id":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_first_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_second_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_first_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_second_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::insert":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by_query":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by_query":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_true":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_false":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::delete_where":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::update":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_when_value_is_null_then_get_null":0.004,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update_one":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update_many":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_one":0.009,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_many":0}} \ No newline at end of file diff --git a/composer.json b/composer.json index 5598b22..aceba23 100644 --- a/composer.json +++ b/composer.json @@ -12,14 +12,15 @@ "require": { "php": ">=8.4", "ext-json": "*", - "ext-pdo": "*" + "ext-pdo": "*", + "tcds-io/php-better-generics": "dev-main" }, "require-dev": { "infection/infection": "^0.29", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^12.1", - "slevomat/coding-standard": "^8.18", - "symfony/var-dumper": "^7.2" + "symfony/var-dumper": "^7.2", + "squizlabs/php_codesniffer": "^3.0" }, "autoload": { "psr-4": { @@ -28,20 +29,20 @@ }, "autoload-dev": { "psr-4": { - "Test\\Tcds\\Io\\Orm\\": "tests/" + "Test\\Tcds\\Io\\Orm\\": "tests" } }, "scripts": { "cs:check": "vendor/bin/phpcs -s --colors --runtime-set testVersion 8.4", "cs:fix": "vendor/bin/phpcbf --colors -ps", - "mutation": "vendor/bin/infection --threads=2 --min-msi=100 --ansi", + "test:mutation": "vendor/bin/infection --threads=2 --min-msi=100 --ansi", "test:stan": "php -d memory_limit=-1 vendor/bin/phpstan analyse src --level=max --ansi", "test:unit": "vendor/bin/phpunit --testdox --color=always", "tests": [ "@cs:check", "@test:stan", "@test:unit", - "@mutation" + "@test:mutation" ] }, "config": { diff --git a/composer.lock b/composer.lock index d82b9a1..517b8f6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,58 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4d9489c6eaf5f9f621bd8bef13d92d3e", - "packages": [], + "content-hash": "560775ef7eb8334cf9e924d3c46cf14c", + "packages": [ + { + "name": "tcds-io/php-better-generics", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/tcds-io/php-better-generics.git", + "reference": "c93056f012a9b8538a1b6cb8351d863982e814a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tcds-io/php-better-generics/zipball/c93056f012a9b8538a1b6cb8351d863982e814a0", + "reference": "c93056f012a9b8538a1b6cb8351d863982e814a0", + "shasum": "" + }, + "require": { + "php": "^8.4" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.5", + "slevomat/coding-standard": "^8.15" + }, + "default-branch": true, + "type": "library", + "autoload": { + "files": [ + "src/functional/functions.php" + ], + "psr-4": { + "Tcds\\Io\\Generic\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Thiago Cordeiro", + "email": "source@tcds.io" + } + ], + "description": "PHP library to better work with generics", + "support": { + "issues": "https://github.com/tcds-io/php-better-generics/issues", + "source": "https://github.com/tcds-io/php-better-generics/tree/main" + }, + "time": "2025-05-12T19:14:59+00:00" + } + ], "packages-dev": [ { "name": "colinodell/json5", @@ -240,84 +290,6 @@ ], "time": "2024-05-06T16:37:16+00:00" }, - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "4be43904336affa5c2f70744a348312336afd0da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", - "reference": "4be43904336affa5c2f70744a348312336afd0da", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "*", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0", - "yoast/phpunit-polyfills": "^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "source": "https://github.com/PHPCSStandards/composer-installer" - }, - "time": "2023-01-05T11:28:13+00:00" - }, { "name": "fidry/cpu-core-counter", "version": "1.2.0", @@ -1199,53 +1171,6 @@ }, "time": "2022-02-21T01:04:05+00:00" }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" - }, - "time": "2025-02-19T13:28:12+00:00" - }, { "name": "phpstan/phpstan", "version": "2.1.14", @@ -1640,16 +1565,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.1.4", + "version": "12.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5ee57ad690bda2c487594577600931a99053436c" + "reference": "f93ef2198df8d54b3195bcee381a33be51d8705e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5ee57ad690bda2c487594577600931a99053436c", - "reference": "5ee57ad690bda2c487594577600931a99053436c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f93ef2198df8d54b3195bcee381a33be51d8705e", + "reference": "f93ef2198df8d54b3195bcee381a33be51d8705e", "shasum": "" }, "require": { @@ -1663,7 +1588,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.1.2", + "phpunit/php-code-coverage": "^12.2.1", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", @@ -1717,7 +1642,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.1.4" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.1.5" }, "funding": [ { @@ -1741,7 +1666,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T07:01:56+00:00" + "time": "2025-05-11T06:44:52+00:00" }, { "name": "psr/container", @@ -1848,20 +1773,20 @@ }, { "name": "sanmai/later", - "version": "0.1.5", + "version": "0.1.7", "source": { "type": "git", "url": "https://github.com/sanmai/later.git", - "reference": "cf5164557d19930295892094996f049ea12ba14d" + "reference": "72a82d783864bca90412d8a26c1878f8981fee97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/later/zipball/cf5164557d19930295892094996f049ea12ba14d", - "reference": "cf5164557d19930295892094996f049ea12ba14d", + "url": "https://api.github.com/repos/sanmai/later/zipball/72a82d783864bca90412d8a26c1878f8981fee97", + "reference": "72a82d783864bca90412d8a26c1878f8981fee97", "shasum": "" }, "require": { - "php": ">=7.4" + "php": ">=8.2" }, "require-dev": { "ergebnis/composer-normalize": "^2.8", @@ -1900,7 +1825,7 @@ "description": "Later: deferred wrapper object", "support": { "issues": "https://github.com/sanmai/later/issues", - "source": "https://github.com/sanmai/later/tree/0.1.5" + "source": "https://github.com/sanmai/later/tree/0.1.7" }, "funding": [ { @@ -1908,7 +1833,7 @@ "type": "github" } ], - "time": "2024-12-06T02:36:26+00:00" + "time": "2025-05-11T01:48:00+00:00" }, { "name": "sanmai/pipeline", @@ -2788,83 +2713,18 @@ ], "time": "2025-02-07T05:00:38+00:00" }, - { - "name": "slevomat/coding-standard", - "version": "8.18.0", - "source": { - "type": "git", - "url": "https://github.com/slevomat/coding-standard.git", - "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/f3b23cb9b26301b8c3c7bb03035a1bee23974593", - "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", - "php": "^7.4 || ^8.0", - "phpstan/phpdoc-parser": "^2.1.0", - "squizlabs/php_codesniffer": "^3.12.2" - }, - "require-dev": { - "phing/phing": "3.0.1", - "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.13", - "phpstan/phpstan-deprecation-rules": "2.0.2", - "phpstan/phpstan-phpunit": "2.0.6", - "phpstan/phpstan-strict-rules": "2.0.4", - "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.17|12.1.3" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", - "keywords": [ - "dev", - "phpcs" - ], - "support": { - "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.18.0" - }, - "funding": [ - { - "url": "https://github.com/kukulich", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", - "type": "tidelift" - } - ], - "time": "2025-05-01T09:40:50+00:00" - }, { "name": "squizlabs/php_codesniffer", - "version": "3.12.2", + "version": "3.13.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa" + "reference": "65ff2489553b83b4597e89c3b8b721487011d186" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", - "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/65ff2489553b83b4597e89c3b8b721487011d186", + "reference": "65ff2489553b83b4597e89c3b8b721487011d186", "shasum": "" }, "require": { @@ -2935,7 +2795,7 @@ "type": "thanks_dev" } ], - "time": "2025-04-13T04:10:18+00:00" + "time": "2025-05-11T03:36:00+00:00" }, { "name": "staabm/side-effects-detector", @@ -4162,7 +4022,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "tcds-io/php-better-generics": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/phpcs.xml b/phpcs.xml index a1521e6..625dbc4 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -4,69 +4,16 @@ tests - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - error - - tests/* - - - tests/* - - + tests/* - + + tests/* diff --git a/phpunit.xml b/phpunit.xml index cae05a7..a3722f2 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,24 +1,23 @@ - - - - - src - - - + cacheDirectory=".phpunit.cache" + executionOrder="depends,defects" + beStrictAboutOutputDuringTests="true" + displayDetailsOnPhpunitDeprecations="true" + failOnPhpunitDeprecation="true" + failOnRisky="true" + failOnWarning="true"> - + tests + + + src + + diff --git a/src/Column/BoolColumn.php b/src/Column/BoolColumn.php index edff561..6fc25a7 100644 --- a/src/Column/BoolColumn.php +++ b/src/Column/BoolColumn.php @@ -4,10 +4,21 @@ namespace Tcds\Io\Orm\Column; +use Override; + /** * @template Entry of object * @extends Column */ readonly class BoolColumn extends Column { + #[Override] public function plain($entry): bool + { + return parent::plain($entry); + } + + #[Override] public function value(array $row): bool + { + return filter_var(parent::value($row), FILTER_VALIDATE_BOOLEAN); + } } diff --git a/src/Column/Column.php b/src/Column/Column.php index d3ed1af..d2828c7 100644 --- a/src/Column/Column.php +++ b/src/Column/Column.php @@ -7,7 +7,7 @@ use Closure; /** - * @template Entry of object + * @template Entry * @template Type */ abstract readonly class Column @@ -23,10 +23,30 @@ public function __construct( /** * @param Entry $entry - * @return Type + * @return Type|null */ - public function valueOn($entry) + public function plain($entry) { return ($this->value)($entry); } + + /** + * @param array $row + * @return Type + */ + public function value(array $row) + { + return $row[$this->name]; + } + + /** + * @param array $row + * @return Type|null + */ + public function nullable(array $row) + { + return ($row[$this->name] ?? null) + ? static::value($row) + : null; + } } diff --git a/src/Column/DateColumn.php b/src/Column/DateColumn.php new file mode 100644 index 0000000..d397ce4 --- /dev/null +++ b/src/Column/DateColumn.php @@ -0,0 +1,28 @@ + + */ +readonly class DateColumn extends Column +{ + public const string FORMAT = 'Y-m-d'; + + #[Override] public function plain($entry): ?string + { + return parent::plain($entry)?->format(self::FORMAT); + } + + #[Override] public function value(array $row): DateTime + { + return new DateTime(parent::value($row)); + } +} diff --git a/src/Column/DateTimeColumn.php b/src/Column/DateTimeColumn.php new file mode 100644 index 0000000..21bd586 --- /dev/null +++ b/src/Column/DateTimeColumn.php @@ -0,0 +1,26 @@ + + */ +readonly class DateTimeColumn extends Column +{ + #[Override] public function plain($entry) + { + return parent::plain($entry)?->format(DateTimeInterface::ATOM); + } + + #[Override] public function value(array $row): DateTime + { + return new DateTime(parent::value($row)); + } +} diff --git a/src/Column/DateTimeImmutableColumn.php b/src/Column/DateTimeImmutableColumn.php new file mode 100644 index 0000000..d1745da --- /dev/null +++ b/src/Column/DateTimeImmutableColumn.php @@ -0,0 +1,26 @@ + + */ +readonly class DateTimeImmutableColumn extends Column +{ + #[Override] public function plain($entry): ?string + { + return parent::plain($entry)?->format(DateTimeInterface::ATOM); + } + + #[Override] public function value(array $row): DateTimeImmutable + { + return new DateTimeImmutable(parent::value($row)); + } +} diff --git a/src/Column/EnumColumn.php b/src/Column/EnumColumn.php new file mode 100644 index 0000000..2ab29ac --- /dev/null +++ b/src/Column/EnumColumn.php @@ -0,0 +1,42 @@ + + */ +readonly class EnumColumn extends Column +{ + /** + * @param class-string $class + */ + public function __construct( + private string $class, + string $name, + Closure $value, + ) { + parent::__construct($name, $value); + } + + #[Override] public function plain($entry): ?string + { + return parent::plain($entry)?->value; + } + + /** + * @param array $row + * @return Enum + */ + #[Override] public function value(array $row) + { + return $this->class::from(parent::value($row)); + } +} diff --git a/src/Column/NumericColumn.php b/src/Column/FloatColumn.php similarity index 57% rename from src/Column/NumericColumn.php rename to src/Column/FloatColumn.php index 46b2aff..3a05778 100644 --- a/src/Column/NumericColumn.php +++ b/src/Column/FloatColumn.php @@ -6,8 +6,8 @@ /** * @template Entry of object - * @extends Column + * @extends Column */ -readonly class NumericColumn extends Column +readonly class FloatColumn extends Column { } diff --git a/src/Column/Nullable/NullableBoolColumn.php b/src/Column/Nullable/NullableBoolColumn.php deleted file mode 100644 index 327ee26..0000000 --- a/src/Column/Nullable/NullableBoolColumn.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -readonly class NullableBoolColumn extends Column -{ -} diff --git a/src/Column/Nullable/NullableIntegerColumn.php b/src/Column/Nullable/NullableIntegerColumn.php deleted file mode 100644 index 903bb60..0000000 --- a/src/Column/Nullable/NullableIntegerColumn.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -readonly class NullableIntegerColumn extends Column -{ -} diff --git a/src/Column/Nullable/NullableNumericColumn.php b/src/Column/Nullable/NullableNumericColumn.php deleted file mode 100644 index c46b9ae..0000000 --- a/src/Column/Nullable/NullableNumericColumn.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -readonly class NullableNumericColumn extends Column -{ -} diff --git a/src/Column/Nullable/NullableStringColumn.php b/src/Column/Nullable/NullableStringColumn.php deleted file mode 100644 index b77e0a9..0000000 --- a/src/Column/Nullable/NullableStringColumn.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -readonly class NullableStringColumn extends Column -{ -} diff --git a/src/EntityRecordMapper.php b/src/EntityRecordMapper.php index 5254436..02d8970 100644 --- a/src/EntityRecordMapper.php +++ b/src/EntityRecordMapper.php @@ -7,7 +7,7 @@ use Tcds\Io\Orm\Column\Column; /** - * @template T of object + * @template T * @template FK of int|string * @extends RecordMapper */ diff --git a/src/EntityRecordRepository.php b/src/EntityRecordRepository.php index ee8865e..caada35 100644 --- a/src/EntityRecordRepository.php +++ b/src/EntityRecordRepository.php @@ -7,8 +7,8 @@ use Tcds\Io\Orm\Connection\Connection; /** - * @template T of object - * @template FK of int|string + * @template T + * @template FK * @extends RecordMapper * @internal */ @@ -18,41 +18,58 @@ abstract class EntityRecordRepository extends RecordRepository * @param EntityRecordMapper $entityMapper */ public function __construct( - Connection $connection, protected EntityRecordMapper $entityMapper, + Connection $connection, + string $table, ) { - parent::__construct($connection, $entityMapper); + parent::__construct($entityMapper, $connection, $table); } /** * @param FK $id * @return T|null */ - public function loadById($id) + public function selectEntityById($id) + { + return $this->selectOneWhere(['id' => $id]); + } + + /** + * @param T $entity + */ + public function updateOne($entity): void { - return $this->loadBy(['id' => $id]); + $this->updateWhere( + $this->mapper->plain($entity), + ['id' => $this->entityMapper->primaryKey->plain($entity)], + ); } /** * @param T ...$entities */ - public function update(...$entities): void + public function updateMany(...$entities): void { foreach ($entities as $entity) { - $this->updateWhere( - $this->mapper->values($entity), - ['id' => $this->entityMapper->primaryKey->valueOn($entity)], - ); + $this->updateOne($entity); } } + /** + * @param T $entity + */ + public function deleteOne($entity): void + { + $this->deleteWhere(['id' => $this->entityMapper->primaryKey->plain($entity)]); + } + /** * @param T ...$entities */ - public function delete(...$entities): void + public function deleteMany(...$entities): void { foreach ($entities as $entity) { - $this->deleteWhere(['id' => $this->entityMapper->primaryKey->valueOn($entity)]); + $this->deleteOne($entity); } } } diff --git a/src/RecordMapper.php b/src/RecordMapper.php index ee21fd9..54ad30a 100644 --- a/src/RecordMapper.php +++ b/src/RecordMapper.php @@ -7,7 +7,7 @@ use Tcds\Io\Orm\Column\Column; /** - * @template T of object + * @template T * @extends RecordRepository */ abstract class RecordMapper @@ -15,9 +15,6 @@ abstract class RecordMapper /** @use TableColumn */ use TableColumn; - /** @use TableColumnNullable */ - use TableColumnNullable; - /** @var list> */ public private(set) array $columns = []; @@ -25,16 +22,16 @@ abstract class RecordMapper * @param array $row * @return T */ - abstract public function entry(array $row); + abstract public function map(array $row); /** * @param T $entry * @return array */ - public function values($entry): array + public function plain($entry): array { $entries = array_map( - fn(Column $column) => [$column->name => $column->valueOn($entry)], + fn(Column $column) => [$column->name => $column->plain($entry)], $this->columns, ); diff --git a/src/RecordRepository.php b/src/RecordRepository.php index 563a9e5..60188b9 100644 --- a/src/RecordRepository.php +++ b/src/RecordRepository.php @@ -10,27 +10,25 @@ use Traversable; /** - * @template T of object - * @internal + * @template T */ abstract class RecordRepository { - abstract protected string $table { get; } - public function __construct( - protected Connection $connection, - protected RecordMapper $mapper, + protected readonly RecordMapper $mapper, + protected readonly Connection $connection, + protected readonly string $table, ) { } /** * @param T $entry */ - public function insert($entry): void + public function insertOne($entry): void { $sql = "INSERT INTO $this->table ({$this->mapper->names()}) VALUES ({$this->bindings()})"; - $values = $this->mapper->values($entry); + $values = $this->mapper->plain($entry); $this->connection->write($sql, $values); } @@ -39,7 +37,7 @@ public function insert($entry): void * @param array $where * @return T|null */ - public function loadBy(array $where) + public function selectOneWhere(array $where) { [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); $sql = trim("SELECT * FROM $this->table$whereColumnsString LIMIT 1"); @@ -48,28 +46,28 @@ public function loadBy(array $where) /** @var array $item */ $item = $items->fetch(PDO::FETCH_ASSOC); - return $item ? $this->mapper->entry($item) : null; + return $item ? $this->mapper->map($item) : null; } /** * @param array $bindings * @return T|null */ - public function loadByQuery(string $selectQuery, array $bindings) + public function selectOneByQuery(string $selectQuery, array $bindings) { $items = $this->connection->read($selectQuery, $bindings); /** @var array $item */ $item = $items->fetch(PDO::FETCH_ASSOC); - return $item ? $this->mapper->entry($item) : null; + return $item ? $this->mapper->map($item) : null; } /** * @param array $where * @return Traversable */ - public function listBy(array $where = [], ?int $limit = null, ?int $offset = null): Traversable + public function selectManyWhere(array $where = [], ?int $limit = null, ?int $offset = null): Traversable { [$whereColumnsString, $whereBindings] = $this->prepareWhere($where); $limitOffset = $this->prepareLimitOffset($limit, $offset); @@ -78,7 +76,7 @@ public function listBy(array $where = [], ?int $limit = null, ?int $offset = nul while ($item = $items->fetch(PDO::FETCH_ASSOC)) { /** @var array $item */ - yield $this->mapper->entry($item); + yield $this->mapper->map($item); } } @@ -86,22 +84,22 @@ public function listBy(array $where = [], ?int $limit = null, ?int $offset = nul * @param array $bindings * @return Traversable */ - public function listByQuery(string $selectQuery, array $bindings): Traversable + public function selectManyByQuery(string $selectQuery, array $bindings): Traversable { $items = $this->connection->read($selectQuery, $bindings); while ($item = $items->fetch(PDO::FETCH_ASSOC)) { /** @var array $item */ - yield $this->mapper->entry($item); + yield $this->mapper->map($item); } } /** * @param array $where */ - public function exists(array $where): bool + public function existsWhere(array $where): bool { - return $this->loadBy($where) !== null; + return $this->selectOneWhere($where) !== null; } /** diff --git a/src/TableColumn.php b/src/TableColumn.php index 76bef92..541dfc4 100644 --- a/src/TableColumn.php +++ b/src/TableColumn.php @@ -4,14 +4,20 @@ namespace Tcds\Io\Orm; +use BackedEnum; use Closure; +use DateTimeInterface; use Tcds\Io\Orm\Column\BoolColumn; +use Tcds\Io\Orm\Column\DateColumn; +use Tcds\Io\Orm\Column\DateTimeColumn; +use Tcds\Io\Orm\Column\DateTimeImmutableColumn; +use Tcds\Io\Orm\Column\EnumColumn; +use Tcds\Io\Orm\Column\FloatColumn; use Tcds\Io\Orm\Column\IntegerColumn; -use Tcds\Io\Orm\Column\NumericColumn; use Tcds\Io\Orm\Column\StringColumn; /** - * @template T of object + * @template T */ trait TableColumn { @@ -41,11 +47,11 @@ protected function boolean(string $name, Closure $value): BoolColumn /** * @param Closure(T $entry): numeric $value - * @return NumericColumn + * @return FloatColumn */ - protected function numeric(string $name, Closure $value): NumericColumn + protected function numeric(string $name, Closure $value): FloatColumn { - $column = new NumericColumn($name, $value); + $column = new FloatColumn($name, $value); $this->columns[] = $column; return $column; @@ -62,4 +68,54 @@ protected function integer(string $name, Closure $value): IntegerColumn return $column; } + + /** + * @param Closure(T $entry): DateTimeInterface $value + * @return DateColumn + */ + protected function date(string $name, Closure $value): DateColumn + { + $column = new DateColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): DateTimeInterface $value + * @return DateTimeColumn + */ + protected function datetime(string $name, Closure $value): DateTimeColumn + { + $column = new DateTimeColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @param Closure(T $entry): DateTimeInterface $value + * @return DateTimeImmutableColumn + */ + protected function datetimeImmutable(string $name, Closure $value): DateTimeImmutableColumn + { + $column = new DateTimeImmutableColumn($name, $value); + $this->columns[] = $column; + + return $column; + } + + /** + * @template E of BackedEnum + * @param class-string $class + * @param Closure(T $entry): DateTimeInterface $value + * @return EnumColumn + */ + protected function enum(string $class, string $name, Closure $value): EnumColumn + { + $column = new EnumColumn($class, $name, $value); + $this->columns[] = $column; + + return $column; + } } diff --git a/src/TableColumnNullable.php b/src/TableColumnNullable.php deleted file mode 100644 index 7221c6b..0000000 --- a/src/TableColumnNullable.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ - protected function nullableString(string $name, Closure $value): NullableStringColumn - { - $column = new NullableStringColumn($name, $value); - $this->columns[] = $column; - - return $column; - } - - /** - * @param Closure(T $entry): bool $value - * @return NullableBoolColumn - */ - protected function nullableBoolean(string $name, Closure $value): NullableBoolColumn - { - $column = new NullableBoolColumn($name, $value); - $this->columns[] = $column; - - return $column; - } - - /** - * @param Closure(T $entry): numeric $value - * @return NullableNumericColumn - */ - protected function nullableNumeric(string $name, Closure $value): NullableNumericColumn - { - $column = new NullableNumericColumn($name, $value); - $this->columns[] = $column; - - return $column; - } - - /** - * @param Closure(T $entry): int $value - * @return NullableIntegerColumn - */ - protected function nullableInteger(string $name, Closure $value): NullableIntegerColumn - { - $column = new NullableIntegerColumn($name, $value); - $this->columns[] = $column; - - return $column; - } -} diff --git a/tests/Fixtures/Address.php b/tests/Fixtures/Address.php index c6c267d..6d8e847 100644 --- a/tests/Fixtures/Address.php +++ b/tests/Fixtures/Address.php @@ -4,36 +4,48 @@ namespace Test\Tcds\Io\Orm\Fixtures; +use DateTime; +use DateTimeImmutable; + readonly class Address { public function __construct( - public string $id, + public int $id, public string $street, public float $number, public int $floor, public bool $active, + public AddressType $type, + public DateTime $createdAt, + public ?DateTimeImmutable $deletedAt, ) { } public static function first(): self { return new self( - id: 'address-1', + id: 1, street: 'First Avenue', number: 145.45, floor: 1, active: true, + type: AddressType::RESIDENCE, + createdAt: new DateTime('2025-05-01 10:15:20'), + deletedAt: new DateTimeImmutable('2025-05-10 11:16:30'), ); } public static function second(): self { return new self( - id: 'address-2', + id: 2, street: 'Second Avenue', number: 34.90, floor: 5, active: false, + type: AddressType::WORK, + createdAt: new DateTime('2025-05-02 20:25:30'), + deletedAt: null, ); } @@ -43,11 +55,14 @@ public static function second(): self public static function firstRowData(): array { return [ - 'id' => 'address-1', + 'id' => 1, 'street' => 'First Avenue', 'number' => 145.45, 'floor' => 1, 'active' => true, + 'type' => 'RESIDENCE', + 'created_at' => '2025-05-01T10:15:20+00:00', + 'deleted_at' => '2025-05-10T11:16:30+00:00', ]; } @@ -57,11 +72,14 @@ public static function firstRowData(): array public static function secondRowData(): array { return [ - 'id' => 'address-2', + 'id' => 2, 'street' => 'Second Avenue', 'number' => 34.90, 'floor' => 5, 'active' => false, + 'type' => 'WORK', + 'created_at' => '2025-05-02T20:25:30+00:00', + 'deleted_at' => null, ]; } } diff --git a/tests/Fixtures/AddressEntityRecordMapper.php b/tests/Fixtures/AddressEntityRecordMapper.php deleted file mode 100644 index 0eb8e75..0000000 --- a/tests/Fixtures/AddressEntityRecordMapper.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -final class AddressEntityRecordMapper extends EntityRecordMapper -{ - public function __construct() - { - parent::__construct($this->string('id', fn(Address $entity) => $entity->id)); - - $this->string('street', fn(Address $entity) => $entity->street); - $this->numeric('number', fn(Address $entity) => $entity->number); - $this->integer('floor', fn(Address $entity) => $entity->floor); - $this->boolean('active', fn(Address $entity) => $entity->active); - } - - /** - * @param array $row - */ - public function entry(array $row): Address - { - return new Address( - id: $row['id'], - street: $row['street'], - number: $row['number'], - floor: $row['floor'], - active: $row['active'], - ); - } -} diff --git a/tests/Fixtures/AddressMapper.php b/tests/Fixtures/AddressMapper.php new file mode 100644 index 0000000..b820478 --- /dev/null +++ b/tests/Fixtures/AddressMapper.php @@ -0,0 +1,55 @@ +id = $this->integer('id', fn(Address $entry) => $entry->id); + $this->street = $this->string('street', fn(Address $entity) => $entity->street); + $this->number = $this->numeric('number', fn(Address $entity) => $entity->number); + $this->floor = $this->integer('floor', fn(Address $entity) => $entity->floor); + $this->active = $this->boolean('active', fn(Address $entity) => $entity->active); + $this->type = $this->enum(AddressType::class, 'type', fn(Address $entity) => $entity->type); + $this->createdAt = $this->datetime('created_at', fn(Address $entity) => $entity->createdAt); + $this->deletedAt = $this->datetimeImmutable('deleted_at', fn(Address $entity) => $entity->deletedAt); + } + + /** + * @param array $row + */ + public function map(array $row): Address + { + return new Address( + id: $this->id->value($row), + street: $this->street->value($row), + number: $this->number->value($row), + floor: $this->floor->value($row), + active: $this->active->value($row), + type: $this->type->value($row), + createdAt: $this->createdAt->value($row), + deletedAt: $this->deletedAt->nullable($row), + ); + } +} diff --git a/tests/Fixtures/AddressRecordMapper.php b/tests/Fixtures/AddressRecordMapper.php deleted file mode 100644 index aa235e1..0000000 --- a/tests/Fixtures/AddressRecordMapper.php +++ /dev/null @@ -1,38 +0,0 @@ -string("id", fn(Address $entity) => $entity->id); - $this->string("street", fn(Address $entity) => $entity->street); - } - - public function table(): string - { - return "addresses"; - } - - /** - * @param array $row - */ - public function entry(array $row): Address - { - return new Address( - id: $row['id'], - street: $row['street'], - number: $row['number'], - floor: $row['floor'], - active: $row['active'], - ); - } -} diff --git a/tests/Fixtures/AddressRepository.php b/tests/Fixtures/AddressRepository.php new file mode 100644 index 0000000..1ccfb65 --- /dev/null +++ b/tests/Fixtures/AddressRepository.php @@ -0,0 +1,26 @@ + + */ +class AddressRepository extends RecordRepository +{ + public function __construct(Connection $connection, RecordMapper $mapper) + { + parent::__construct($mapper, $connection, 'addresses'); + } + + public function loadById(int $id): Address + { + return $this->selectOneWhere(['id' => $id]) ?? throw new Exception('Address not found'); + } +} diff --git a/tests/Fixtures/AddressType.php b/tests/Fixtures/AddressType.php new file mode 100644 index 0000000..5a2b1f9 --- /dev/null +++ b/tests/Fixtures/AddressType.php @@ -0,0 +1,11 @@ + + */ + public static function firstData(): array + { + return [ + 'id' => 1, + 'name' => 'First User', + 'date_of_birth' => '2020-01-01', + 'address_id' => 1, + ]; + } + + /** + * @return array + */ + public static function secondData(): array + { + return [ + 'id' => 2, + 'name' => 'Second User', + 'date_of_birth' => '2022-10-15', + 'address_id' => 2, + ]; + } +} diff --git a/tests/Fixtures/UserMapper.php b/tests/Fixtures/UserMapper.php new file mode 100644 index 0000000..56d94a8 --- /dev/null +++ b/tests/Fixtures/UserMapper.php @@ -0,0 +1,35 @@ + + */ +final class UserMapper extends EntityRecordMapper +{ + public function __construct( + private readonly AddressRepository $addressRepository, + ) { + parent::__construct($this->string('id', fn(User $entity) => $entity->id)); + + $this->string('name', fn(User $entity) => $entity->name); + $this->date('date_of_birth', fn(User $entity) => $entity->dateOfBirth); + $this->integer('address_id', fn(User $entity) => $entity->address->id); + } + + #[Override] public function map(array $row): User + { + return new User( + id: $row['id'], + name: $row['name'], + dateOfBirth: new DateTime($row['date_of_birth']), + address: lazyOf(Address::class, fn() => $this->addressRepository->loadById($row['address_id'])), + ); + } +} diff --git a/tests/Fixtures/UserRepository.php b/tests/Fixtures/UserRepository.php new file mode 100644 index 0000000..7425d96 --- /dev/null +++ b/tests/Fixtures/UserRepository.php @@ -0,0 +1,25 @@ + + */ +class UserRepository extends EntityRecordRepository +{ + public function __construct(Connection $connection, UserMapper $mapper) + { + parent::__construct($mapper, $connection, 'users'); + } + + public function loadById(string $id): User + { + return $this->selectEntityById($id) ?? throw new Exception('Address not found'); + } +} diff --git a/tests/Unit/Column/BoolColumnTest.php b/tests/Unit/Column/BoolColumnTest.php new file mode 100644 index 0000000..1d80c97 --- /dev/null +++ b/tests/Unit/Column/BoolColumnTest.php @@ -0,0 +1,40 @@ +column = new BoolColumn('active', fn(object $entry) => $entry->active); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $this->assertTrue($this->column->plain((object) ['active' => true])); + $this->assertFalse($this->column->plain((object) ['active' => false])); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $this->assertTrue($this->column->value(['active' => true])); + $this->assertFalse($this->column->value(['active' => false])); + + $this->assertTrue($this->column->value(['active' => 'true'])); + $this->assertFalse($this->column->value(['active' => 'false'])); + + $this->assertTrue($this->column->value(['active' => 1])); + $this->assertFalse($this->column->value(['active' => 0])); + + $this->assertTrue($this->column->value(['active' => '1'])); + $this->assertFalse($this->column->value(['active' => '0'])); + } +} diff --git a/tests/Unit/Column/ColumnTest.php b/tests/Unit/Column/ColumnTest.php new file mode 100644 index 0000000..b7975ca --- /dev/null +++ b/tests/Unit/Column/ColumnTest.php @@ -0,0 +1,42 @@ +column = new readonly class ('prop', fn(object $entry) => $entry->prop) extends Column + { + #[Override] public function value(array $row): string + { + return parent::value($row) . '-' . parent::value($row); + } + }; + } + + #[Test] public function given_row_when_prop_is_not_null_then_return_value(): void + { + $this->assertEquals('bar-bar', $this->column->nullable(['prop' => 'bar'])); + } + + #[Test] public function given_row_when_prop_is_null_then_return_null(): void + { + $this->assertNull($this->column->nullable(['prop' => null])); + } + + #[Test] public function given_row_when_prop_is_unset_null_then_return_null(): void + { + $this->assertNull($this->column->nullable([])); + } +} diff --git a/tests/Unit/Column/DateColumnTest.php b/tests/Unit/Column/DateColumnTest.php new file mode 100644 index 0000000..d577cdb --- /dev/null +++ b/tests/Unit/Column/DateColumnTest.php @@ -0,0 +1,48 @@ +column = new DateColumn('created_at', fn(object $entry) => $entry->created_at); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['created_at' => new DateTime('2025-05-08')]; + + $plain = $this->column->plain($entry); + + $this->assertEquals('2025-05-08', $plain); + } + + #[Test] public function given_an_entry_when_value_is_null_then_get_null(): void + { + $entry = (object) ['created_at' => null]; + + $plain = $this->column->plain($entry); + + $this->assertNull($plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['created_at' => '2025-05-08']; + + $value = $this->column->value($row); + + $this->assertEquals(DateTime::class, $value::class); + $this->assertEquals(new DateTime('2025-05-08'), $value); + } +} diff --git a/tests/Unit/Column/DateTimeColumnTest.php b/tests/Unit/Column/DateTimeColumnTest.php new file mode 100644 index 0000000..71c558b --- /dev/null +++ b/tests/Unit/Column/DateTimeColumnTest.php @@ -0,0 +1,48 @@ +column = new DateTimeColumn('created_at', fn(object $entry) => $entry->created_at); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['created_at' => new DateTime('2025-05-08T18:46:20')]; + + $plain = $this->column->plain($entry); + + $this->assertEquals('2025-05-08T18:46:20+00:00', $plain); + } + + #[Test] public function given_an_entry_when_value_is_null_then_get_null(): void + { + $entry = (object) ['created_at' => null]; + + $plain = $this->column->plain($entry); + + $this->assertNull($plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['created_at' => '2025-05-08T18:46:20']; + + $value = $this->column->value($row); + + $this->assertEquals(DateTime::class, $value::class); + $this->assertEquals(new DateTime('2025-05-08T18:46:20'), $value); + } +} diff --git a/tests/Unit/Column/DateTimeImmutableColumnTest.php b/tests/Unit/Column/DateTimeImmutableColumnTest.php new file mode 100644 index 0000000..258d779 --- /dev/null +++ b/tests/Unit/Column/DateTimeImmutableColumnTest.php @@ -0,0 +1,48 @@ +column = new DateTimeImmutableColumn('created_at', fn(object $entry) => $entry->created_at); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['created_at' => new DateTimeImmutable('2025-05-08T18:46:20')]; + + $plain = $this->column->plain($entry); + + $this->assertEquals('2025-05-08T18:46:20+00:00', $plain); + } + + #[Test] public function given_an_entry_when_value_is_null_then_get_null(): void + { + $entry = (object) ['created_at' => null]; + + $plain = $this->column->plain($entry); + + $this->assertNull($plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['created_at' => '2025-05-08T18:46:20']; + + $value = $this->column->value($row); + + $this->assertEquals(DateTimeImmutable::class, $value::class); + $this->assertEquals(new DateTimeImmutable('2025-05-08T18:46:20'), $value); + } +} diff --git a/tests/Unit/Column/EnumColumnTest.php b/tests/Unit/Column/EnumColumnTest.php new file mode 100644 index 0000000..ef1af00 --- /dev/null +++ b/tests/Unit/Column/EnumColumnTest.php @@ -0,0 +1,47 @@ +column = new EnumColumn(AddressType::class, 'type', fn(object $entry) => $entry->type); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['type' => AddressType::RESIDENCE]; + + $plain = $this->column->plain($entry); + + $this->assertEquals('RESIDENCE', $plain); + } + + #[Test] public function given_an_entry_when_value_is_null_then_get_null(): void + { + $entry = (object) ['type' => null]; + + $plain = $this->column->plain($entry); + + $this->assertNull($plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['type' => 'RESIDENCE']; + + $value = $this->column->value($row); + + $this->assertEquals(AddressType::RESIDENCE, $value); + } +} diff --git a/tests/Unit/Column/FloatColumnTest.php b/tests/Unit/Column/FloatColumnTest.php new file mode 100644 index 0000000..19f8eff --- /dev/null +++ b/tests/Unit/Column/FloatColumnTest.php @@ -0,0 +1,37 @@ +column = new FloatColumn('height', fn(object $entry) => $entry->height); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['height' => 1.76]; + + $plain = $this->column->plain($entry); + + $this->assertEquals(1.76, $plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['height' => 1.76]; + + $value = $this->column->value($row); + + $this->assertEquals(1.76, $value); + } +} diff --git a/tests/Unit/Column/IntegerColumnTest.php b/tests/Unit/Column/IntegerColumnTest.php new file mode 100644 index 0000000..2c404c9 --- /dev/null +++ b/tests/Unit/Column/IntegerColumnTest.php @@ -0,0 +1,37 @@ +column = new IntegerColumn('age', fn(object $entry) => $entry->age); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['age' => 36]; + + $plain = $this->column->plain($entry); + + $this->assertEquals(36, $plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['age' => 36]; + + $value = $this->column->value($row); + + $this->assertEquals(36, $value); + } +} diff --git a/tests/Unit/Column/StringColumnTest.php b/tests/Unit/Column/StringColumnTest.php new file mode 100644 index 0000000..8d9b47c --- /dev/null +++ b/tests/Unit/Column/StringColumnTest.php @@ -0,0 +1,37 @@ +column = new StringColumn('name', fn(object $entry) => $entry->name); + } + + #[Test] public function given_an_entry_then_get_its_plain_value(): void + { + $entry = (object) ['name' => 'Arthur Dent']; + + $plain = $this->column->plain($entry); + + $this->assertEquals('Arthur Dent', $plain); + } + + #[Test] public function given_an_array_then_get_its_value(): void + { + $row = ['name' => 'Arthur Dent']; + + $value = $this->column->value($row); + + $this->assertEquals('Arthur Dent', $value); + } +} diff --git a/tests/Unit/EntityManagerTest.php b/tests/Unit/EntityManagerTest.php deleted file mode 100644 index 4449dcb..0000000 --- a/tests/Unit/EntityManagerTest.php +++ /dev/null @@ -1,106 +0,0 @@ -connection = $this->createMock(Connection::class); - - $this->manager = new class (new AddressEntityRecordMapper(), $this->connection) extends EntityRecordRepository - { - protected string $table { - get { - return 'entity_table'; - } - } - }; - } - - #[Test] public function load_by_id(): void - { - $this->connection - ->expects($this->once()) - ->method('read') - ->with('SELECT * FROM entity_table WHERE id = :id LIMIT 1', ['id' => 'galaxy-1']); - - $this->manager->loadById('galaxy-1'); - } - - #[Test] public function update(): void - { - $matcher = $this->exactly(2); - - $this->connection - ->expects($matcher) - ->method('write') - ->with($this->consecutive( - $matcher, - [ - 'UPDATE entity_table SET id = :id, street = :street, number = :number, floor = :floor, active = :active WHERE id = :id', - [ - 'id' => 'address-1', - 'street' => 'First Avenue', - 'number' => 145.45, - 'floor' => 1, - 'active' => true, - ], - ], - [ - 'UPDATE entity_table SET id = :id, street = :street, number = :number, floor = :floor, active = :active WHERE id = :id', - [ - 'id' => 'address-2', - 'street' => 'Second Avenue', - 'number' => 34.9, - 'floor' => 5, - 'active' => false, - ], - ], - )); - - $this->manager->update( - Address::first(), - Address::second(), - ); - } - - #[Test] public function delete(): void - { - $matcher = $this->exactly(2); - - $this->connection - ->expects($matcher) - ->method('write') - ->with($this->consecutive( - $matcher, - [ - 'DELETE FROM entity_table WHERE id = :id', - ['id' => 'address-1'], - ], - [ - 'DELETE FROM entity_table WHERE id = :id', - ['id' => 'address-2'], - ], - )); - - $this->manager->delete( - Address::first(), - Address::second(), - ); - } -} diff --git a/tests/Unit/EntityRecordMapperTest.php b/tests/Unit/EntityRecordMapperTest.php new file mode 100644 index 0000000..bca33b2 --- /dev/null +++ b/tests/Unit/EntityRecordMapperTest.php @@ -0,0 +1,75 @@ +addressRepository = $this->createMock(AddressRepository::class); + + $this->mapper = new UserMapper($this->addressRepository); + } + + #[Test] public function get_plain_array_from_first_user(): void + { + $object = User::first(); + + $plain = $this->mapper->plain($object); + + $this->assertSame(User::firstData(), $plain); + } + + #[Test] public function get_plain_array_from_second_user(): void + { + $object = User::second(); + + $values = $this->mapper->plain($object); + + $this->assertSame(User::secondData(), $values); + } + + #[Test] public function map_first_user(): void + { + $this->setupLoadAddress(1, Address::first()); + $data = User::firstData(); + + $object = $this->mapper->map($data); + initializeLazyObject($object->address); + + $this->assertEquals(User::first(), $object); + } + + #[Test] public function map_second_user(): void + { + $this->setupLoadAddress(2, Address::second()); + $data = User::secondData(); + + $object = $this->mapper->map($data); + initializeLazyObject($object->address); + + $this->assertEquals(User::second(), $object); + } + + private function setupLoadAddress(int $userId, Address $address): void + { + $this->addressRepository + ->expects($this->once()) + ->method('loadById') + ->with($userId) + ->willReturn($address); + } +} diff --git a/tests/Unit/EntityRecordRepositoryTest.php b/tests/Unit/EntityRecordRepositoryTest.php new file mode 100644 index 0000000..5204ac4 --- /dev/null +++ b/tests/Unit/EntityRecordRepositoryTest.php @@ -0,0 +1,135 @@ +connection = $this->createMock(Connection::class); + $this->addressRepository = $this->createMock(AddressRepository::class); + + $this->repository = new UserRepository( + $this->connection, + new UserMapper($this->addressRepository), + ); + } + + #[Test] public function load_by_id(): void + { + $this->connection + ->expects($this->once()) + ->method('read') + ->with('SELECT * FROM users WHERE id = :id LIMIT 1', ['id' => 'galaxy-1']); + + $this->repository->selectEntityById('galaxy-1'); + } + + #[Test] public function update_one(): void + { + $this->connection + ->expects($this->exactly(1)) + ->method('write') + ->with( + 'UPDATE users SET id = :id, name = :name, date_of_birth = :date_of_birth, address_id = :address_id WHERE id = :id', + [ + 'id' => 1, + 'name' => 'First User', + 'date_of_birth' => '2020-01-01', + 'address_id' => 1, + ], + ); + + $this->repository->updateOne(User::first()); + } + + #[Test] public function update_many(): void + { + $matcher = $this->exactly(2); + + $this->connection + ->expects($matcher) + ->method('write') + ->with($this->consecutive( + $matcher, + [ + 'UPDATE users SET id = :id, name = :name, date_of_birth = :date_of_birth, address_id = :address_id WHERE id = :id', + [ + 'id' => 1, + 'name' => 'First User', + 'date_of_birth' => '2020-01-01', + 'address_id' => 1, + ], + ], + [ + 'UPDATE users SET id = :id, name = :name, date_of_birth = :date_of_birth, address_id = :address_id WHERE id = :id', + [ + 'id' => 2, + 'name' => 'Second User', + 'date_of_birth' => '2022-10-15', + 'address_id' => 2, + ], + ], + )); + + $this->repository->updateMany( + User::first(), + User::second(), + ); + } + + #[Test] public function delete_one(): void + { + $this->connection + ->expects($this->exactly(1)) + ->method('write') + ->with( + 'DELETE FROM users WHERE id = :id', + ['id' => 1], + ); + + $this->repository->deleteOne(User::first()); + } + + #[Test] public function delete_many(): void + { + $matcher = $this->exactly(2); + + $this->connection + ->expects($matcher) + ->method('write') + ->with($this->consecutive( + $matcher, + [ + 'DELETE FROM users WHERE id = :id', + ['id' => 1], + ], + [ + 'DELETE FROM users WHERE id = :id', + ['id' => 2], + ], + )); + + $this->repository->deleteMany( + User::first(), + User::second(), + ); + } +} diff --git a/tests/Unit/Old/EntityTableMapperDeleteTest.php b/tests/Unit/Old/EntityTableMapperDeleteTest.php deleted file mode 100644 index 0498bb5..0000000 --- a/tests/Unit/Old/EntityTableMapperDeleteTest.php +++ /dev/null @@ -1,35 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->table = new AddressEntityRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $address = new Address(id: 'address-xxx', street: "Galaxy Avenue"); - - $this->connection - ->expects($this->once()) - ->method('write') - ->with("DELETE FROM addresses WHERE id = :id", [':id' => 'address-xxx']); - - $this->table->delete($address); - } -} diff --git a/tests/Unit/Old/EntityTableMapperLoadByIdTest.php b/tests/Unit/Old/EntityTableMapperLoadByIdTest.php deleted file mode 100644 index 43fcbce..0000000 --- a/tests/Unit/Old/EntityTableMapperLoadByIdTest.php +++ /dev/null @@ -1,42 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressEntityRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $this->statement - ->method('fetch') - ->willReturn(['id' => 'address-xxx', 'street' => "Galaxy Avenue"]); - $this->connection - ->expects($this->once()) - ->method('read') - ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", [':id' => 'address-xxx']) - ->willReturn($this->statement); - - $result = $this->table->loadById('address-xxx'); - - $this->assertEquals(new Address(id: 'address-xxx', street: "Galaxy Avenue"), $result); - } -} diff --git a/tests/Unit/Old/EntityTableMapperUpdateTest.php b/tests/Unit/Old/EntityTableMapperUpdateTest.php deleted file mode 100644 index 0a7337c..0000000 --- a/tests/Unit/Old/EntityTableMapperUpdateTest.php +++ /dev/null @@ -1,38 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->table = new AddressEntityRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $address = new Address(id: 'address-xxx', street: "Galaxy Avenue"); - - $this->connection - ->expects($this->once()) - ->method('write') - ->with( - "UPDATE addresses SET id = :id, street = :street WHERE id = :id", - [':street' => 'Galaxy Avenue', ':id' => 'address-xxx'], - ); - - $this->table->update($address); - } -} diff --git a/tests/Unit/Old/TableMapperDeleteWhereTest.php b/tests/Unit/Old/TableMapperDeleteWhereTest.php deleted file mode 100644 index ec62025..0000000 --- a/tests/Unit/Old/TableMapperDeleteWhereTest.php +++ /dev/null @@ -1,35 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $this->connection - ->expects($this->once()) - ->method('write') - ->with( - "DELETE FROM addresses WHERE id = :id", - [':id' => 'address-xxx'], - ); - - $this->table->deleteWhere(['id' => 'address-xxx']); - } -} diff --git a/tests/Unit/Old/TableMapperExistsTest.php b/tests/Unit/Old/TableMapperExistsTest.php deleted file mode 100644 index 9f28dd8..0000000 --- a/tests/Unit/Old/TableMapperExistsTest.php +++ /dev/null @@ -1,57 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenTheConditionsWhenSelectReturnsAnEntryThenExistIsTrue(): void - { - $this->statement - ->method('fetch') - ->willReturn(['id' => 'address-xxx', 'street' => "Galaxy Avenue"]); - $this->connection - ->expects($this->once()) - ->method('read') - ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", [':id' => 'address-xxx']) - ->willReturn($this->statement); - - $exists = $this->table->exists(['id' => 'address-xxx']); - - $this->assertTrue($exists); - } - - public function testGivenTheConditionsWhenSelectReturnsNullThenExistIsFalse(): void - { - $this->statement - ->method('fetch') - ->willReturn(null); - $this->connection - ->expects($this->once()) - ->method('read') - ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", [':id' => 'address-xxx']) - ->willReturn($this->statement); - - $exists = $this->table->exists(['id' => 'address-xxx']); - - $this->assertFalse($exists); - } -} diff --git a/tests/Unit/Old/TableMapperInsertTest.php b/tests/Unit/Old/TableMapperInsertTest.php deleted file mode 100644 index 57b9591..0000000 --- a/tests/Unit/Old/TableMapperInsertTest.php +++ /dev/null @@ -1,38 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenAnEntryThenRunInsertWithItsData(): void - { - $address = new Address("address-xxx", "Galaxy Avenue"); - - $this->connection - ->expects($this->once()) - ->method('write') - ->with( - "INSERT INTO addresses (id, street) VALUES (:id, :street)", - ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], - ); - - $this->table->insert($address); - } -} diff --git a/tests/Unit/Old/TableMapperLoadByQueryTest.php b/tests/Unit/Old/TableMapperLoadByQueryTest.php deleted file mode 100644 index 690ec15..0000000 --- a/tests/Unit/Old/TableMapperLoadByQueryTest.php +++ /dev/null @@ -1,42 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenASqlQueryAndItsBindingsThenBypassTheQueryToTheConnectionAndReturnTheFirstItem(): void - { - $this->statement - ->method('fetch') - ->willReturn(['id' => 'address-xxx', 'street' => "Galaxy Avenue"]); - $this->connection - ->expects($this->once()) - ->method('read') - ->with("select * from addresses where id LIKE :id", [':id' => 'address-xxx']) - ->willReturn($this->statement); - - $result = $this->table->loadByQuery("select * from addresses where id LIKE :id", [':id' => 'address-xxx']); - - $this->assertEquals(new Address(id: 'address-xxx', street: "Galaxy Avenue"), $result); - } -} diff --git a/tests/Unit/Old/TableMapperLoadByTest.php b/tests/Unit/Old/TableMapperLoadByTest.php deleted file mode 100644 index afe896e..0000000 --- a/tests/Unit/Old/TableMapperLoadByTest.php +++ /dev/null @@ -1,45 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $this->statement - ->method('fetch') - ->willReturn(['id' => 'address-xxx', 'street' => "Galaxy Avenue"]); - $this->connection - ->expects($this->once()) - ->method('read') - ->with( - "SELECT * FROM addresses WHERE id = :id LIMIT 1", - [':id' => 'address-xxx'], - ) - ->willReturn($this->statement); - - $result = $this->table->loadBy(['id' => 'address-xxx']); - - $this->assertEquals(new Address(id: 'address-xxx', street: "Galaxy Avenue"), $result); - } -} diff --git a/tests/Unit/Old/TableMapperSelectByQueryTest.php b/tests/Unit/Old/TableMapperSelectByQueryTest.php deleted file mode 100644 index 6c12c2f..0000000 --- a/tests/Unit/Old/TableMapperSelectByQueryTest.php +++ /dev/null @@ -1,56 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenASqlQueryAndItsBindingsThenBypassTheQueryToTheConnectionAndReturnTheItemList(): void - { - $this->statement - ->method('fetch') - ->willReturnOnConsecutiveCalls( - ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], - ['id' => 'address-yyy', 'street' => "Galaxy Highway"], - null, - ); - - $this->connection - ->expects($this->once()) - ->method('read') - ->with("select * from addresses where street LIKE :street", [':street' => 'Galaxy%']) - ->willReturn($this->statement); - - $result = $this->table->findByQuery( - "select * from addresses where street LIKE :street", - [':street' => 'Galaxy%'], - ); - - $this->assertEquals( - [ - new Address(id: 'address-xxx', street: "Galaxy Avenue"), - new Address(id: 'address-yyy', street: "Galaxy Highway"), - ], - iterator_to_array($result), - ); - } -} diff --git a/tests/Unit/Old/TableMapperSelectTest.php b/tests/Unit/Old/TableMapperSelectTest.php deleted file mode 100644 index 9f2ea2b..0000000 --- a/tests/Unit/Old/TableMapperSelectTest.php +++ /dev/null @@ -1,105 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->statement = $this->createMock(PDOStatement::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenWhereConditionWhenNotEmptyThenSelectWithWhereStatement(): void - { - $this->statement - ->method('fetch') - ->willReturnOnConsecutiveCalls(['id' => 'address-xxx', 'street' => "Galaxy Avenue"], null); - - $this->connection - ->expects($this->once()) - ->method('read') - ->with( - "SELECT * FROM addresses WHERE id = :id AND street = :street", - [':id' => 'address-xxx', ':street' => "Galaxy Avenue"], - ) - ->willReturn($this->statement); - - $result = $this->table->findBy(['id' => 'address-xxx', 'street' => 'Galaxy Avenue']); - - $this->assertEquals( - [ - new Address(id: 'address-xxx', street: "Galaxy Avenue"), - ], - iterator_to_array($result), - ); - } - - public function testGivenWhereConditionWhenEmptyThenSelectWithoutWhereStatement(): void - { - $this->statement - ->method('fetch') - ->willReturnOnConsecutiveCalls( - ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], - ['id' => 'address-yyy', 'street' => "Galaxy Highway"], - null, - ); - - $this->connection - ->expects($this->once()) - ->method('read') - ->with("SELECT * FROM addresses", []) - ->willReturn($this->statement); - - $result = $this->table->findBy([]); - - $this->assertEquals( - [ - new Address(id: 'address-xxx', street: "Galaxy Avenue"), - new Address(id: 'address-yyy', street: "Galaxy Highway"), - ], - iterator_to_array($result), - ); - } - - public function testGivenTheLimitAndOffsetThenSelectWithLimitOffsetStatement(): void - { - $this->statement - ->method('fetch') - ->willReturnOnConsecutiveCalls( - ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], - ['id' => 'address-yyy', 'street' => "Galaxy Highway"], - null, - ); - - $this->connection - ->expects($this->once()) - ->method('read') - ->with("SELECT * FROM addresses LIMIT 5 OFFSET 15", []) - ->willReturn($this->statement); - - $result = $this->table->findBy(where: [], limit: 5, offset: 15); - - $this->assertEquals( - [ - new Address(id: 'address-xxx', street: "Galaxy Avenue"), - new Address(id: 'address-yyy', street: "Galaxy Highway"), - ], - iterator_to_array($result), - ); - } -} diff --git a/tests/Unit/Old/TableMapperTest.php b/tests/Unit/Old/TableMapperTest.php deleted file mode 100644 index ce78148..0000000 --- a/tests/Unit/Old/TableMapperTest.php +++ /dev/null @@ -1,35 +0,0 @@ -createMock(Connection::class); - - $this->table = new AddressRecordMapper($connection); - } - - public function testGetEntryValues(): void - { - $address = new Address(id: 'address-xxx', street: 'Galaxy Avenue'); - - $values = $this->table->values($address); - - $this->assertEquals([ - 'id' => 'address-xxx', - 'street' => 'Galaxy Avenue', - ], $values); - } -} diff --git a/tests/Unit/Old/TableUpdateWhereTest.php b/tests/Unit/Old/TableUpdateWhereTest.php deleted file mode 100644 index f420322..0000000 --- a/tests/Unit/Old/TableUpdateWhereTest.php +++ /dev/null @@ -1,35 +0,0 @@ -connection = $this->createMock(Connection::class); - $this->table = new AddressRecordMapper($this->connection); - } - - public function testGivenWhereWhenNotEmptyWhenRunQueryWithWhereAndLimitAndReturnOnlyOneEntry(): void - { - $this->connection - ->expects($this->once()) - ->method('write') - ->with( - "UPDATE addresses SET street = :street WHERE id = :id", - [':street' => 'Galaxy Avenue', ':id' => 'address-xxx'], - ); - - $this->table->updateWhere(['street' => 'Galaxy Avenue'], ['id' => 'address-xxx']); - } -} diff --git a/tests/Unit/RecordMapperTest.php b/tests/Unit/RecordMapperTest.php new file mode 100644 index 0000000..16fa64a --- /dev/null +++ b/tests/Unit/RecordMapperTest.php @@ -0,0 +1,56 @@ +mapper = new AddressMapper(); + } + + #[Test] public function get_plain_array_from_first_address(): void + { + $object = Address::first(); + + $plain = $this->mapper->plain($object); + + $this->assertSame(Address::firstRowData(), $plain); + } + + #[Test] public function get_plain_array_from_second_address(): void + { + $object = Address::second(); + + $plain = $this->mapper->plain($object); + + $this->assertSame(Address::secondRowData(), $plain); + } + + #[Test] public function map_first_address(): void + { + $data = Address::firstRowData(); + + $object = $this->mapper->map($data); + + $this->assertEquals(Address::first(), $object); + } + + #[Test] public function map_second_address(): void + { + $data = Address::secondRowData(); + + $object = $this->mapper->map($data); + + $this->assertEquals(Address::second(), $object); + } +} diff --git a/tests/Unit/RecordManagerTest.php b/tests/Unit/RecordRepositoryTest.php similarity index 81% rename from tests/Unit/RecordManagerTest.php rename to tests/Unit/RecordRepositoryTest.php index b49b20f..7ca35b9 100644 --- a/tests/Unit/RecordManagerTest.php +++ b/tests/Unit/RecordRepositoryTest.php @@ -10,10 +10,10 @@ use Tcds\Io\Orm\Connection\Connection; use Tcds\Io\Orm\RecordRepository; use Test\Tcds\Io\Orm\Fixtures\Address; -use Test\Tcds\Io\Orm\Fixtures\AddressEntityRecordMapper; +use Test\Tcds\Io\Orm\Fixtures\AddressMapper; use Test\Tcds\Io\Orm\TestCase; -class RecordManagerTest extends TestCase +class RecordRepositoryTest extends TestCase { private Connection&MockObject $connection; private PDOStatement&MockObject $statement; @@ -24,13 +24,8 @@ protected function setUp(): void $this->connection = $this->createMock(Connection::class); $this->statement = $this->createMock(PDOStatement::class); - $this->manager = new class (new AddressEntityRecordMapper(), $this->connection) extends RecordRepository + $this->manager = new class (new AddressMapper(), $this->connection, 'addresses') extends RecordRepository { - protected string $table { - get { - return 'record_table'; - } - } }; } @@ -42,17 +37,20 @@ protected function setUp(): void ->expects($this->once()) ->method('write') ->with( - "INSERT INTO addresses (id, street, number, floor, active) VALUES (:id, :street, :number, :floor, :active)", + "INSERT INTO addresses (id, street, number, floor, active, type, created_at, deleted_at) VALUES (:id, :street, :number, :floor, :active, :type, :created_at, :deleted_at)", [ - 'id' => 'address-1', + 'id' => 1, 'street' => 'First Avenue', 'number' => 145.45, 'floor' => 1, 'active' => true, + 'type' => 'RESIDENCE', + 'created_at' => '2025-05-01T10:15:20+00:00', + 'deleted_at' => '2025-05-10T11:16:30+00:00', ], ); - $this->manager->insert($address); + $this->manager->insertOne($address); } #[Test] public function load_by(): void @@ -69,7 +67,7 @@ protected function setUp(): void ) ->willReturn($this->statement); - $result = $this->manager->loadBy(['id' => 'address-xxx']); + $result = $this->manager->selectOneWhere(['id' => 'address-xxx']); $this->assertEquals(Address::second(), $result); } @@ -85,7 +83,7 @@ protected function setUp(): void ->with("select * from addresses where id LIKE :id", [':id' => 'address-xxx']) ->willReturn($this->statement); - $result = $this->manager->loadByQuery("select * from addresses where id LIKE :id", [':id' => 'address-xxx']); + $result = $this->manager->selectOneByQuery("select * from addresses where id LIKE :id", [':id' => 'address-xxx']); $this->assertEquals(Address::first(), $result); } @@ -109,7 +107,7 @@ protected function setUp(): void ) ->willReturn($this->statement); - $result = $this->manager->listBy( + $result = $this->manager->selectManyWhere( ['id' => 'address-xxx', 'street' => 'Galaxy Avenue'], limit: 5, offset: 15, @@ -129,8 +127,8 @@ public function list_by_returns_multiple_entries(): void $this->statement ->method('fetch') ->willReturnOnConsecutiveCalls( - ['id' => 'address-xxx', 'street' => "Galaxy Avenue"], - ['id' => 'address-yyy', 'street' => "Galaxy Highway"], + Address::firstRowData(), + Address::secondRowData(), null, ); @@ -140,12 +138,12 @@ public function list_by_returns_multiple_entries(): void ->with("SELECT * FROM addresses", []) ->willReturn($this->statement); - $result = $this->manager->listBy(); + $result = $this->manager->selectManyWhere(); $this->assertEquals( [ - new Address(id: 'address-xxx', street: "Galaxy Avenue"), - new Address(id: 'address-yyy', street: "Galaxy Highway"), + Address::first(), + Address::second(), ], iterator_to_array($result), ); @@ -167,7 +165,7 @@ public function list_by_returns_multiple_entries(): void ->with("SELECT * FROM addresses WHERE foo = :foo", ['foo' => 'bar']) ->willReturn($this->statement); - $result = $this->manager->listByQuery('SELECT * FROM addresses WHERE foo = :foo', ['foo' => 'bar']); + $result = $this->manager->selectManyByQuery('SELECT * FROM addresses WHERE foo = :foo', ['foo' => 'bar']); $this->assertEquals( [ @@ -189,7 +187,7 @@ public function list_by_returns_multiple_entries(): void ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", ['id' => 'address-xxx']) ->willReturn($this->statement); - $exists = $this->manager->exists(['id' => 'address-xxx']); + $exists = $this->manager->existsWhere(['id' => 'address-xxx']); $this->assertTrue($exists); } @@ -205,7 +203,7 @@ public function list_by_returns_multiple_entries(): void ->with("SELECT * FROM addresses WHERE id = :id LIMIT 1", ['id' => 'address-xxx']) ->willReturn($this->statement); - $exists = $this->manager->exists(['id' => 'address-xxx']); + $exists = $this->manager->existsWhere(['id' => 'address-xxx']); $this->assertFalse($exists); } From 36e9ed09a5e3ca9119847747bbc7290eb67c7a60 Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 08:28:26 +0200 Subject: [PATCH 3/8] Update composer --- composer.json | 4 ++-- composer.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index aceba23..f2efb17 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ "infection/infection": "^0.29", "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^12.1", - "symfony/var-dumper": "^7.2", - "squizlabs/php_codesniffer": "^3.0" + "squizlabs/php_codesniffer": "^3.0", + "symfony/var-dumper": "^7.2" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 517b8f6..dc35865 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "560775ef7eb8334cf9e924d3c46cf14c", + "content-hash": "b8511f52080ee9ab941d02ca33f14477", "packages": [ { "name": "tcds-io/php-better-generics", From 63a747fcad399a2d74efd68cfa104b60e964e60f Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 11:52:00 +0200 Subject: [PATCH 4/8] Fixed types --- .phpunit.cache/test-results | 1 - src/Column/BoolColumn.php | 6 ++-- src/Column/Column.php | 10 +++--- src/Column/DateColumn.php | 9 ++++-- src/Column/DateTimeColumn.php | 11 ++++--- src/Column/DateTimeImmutableColumn.php | 9 ++++-- src/Column/EnumColumn.php | 19 ++++++----- src/Column/FloatColumn.php | 4 +-- src/Column/IntegerColumn.php | 4 +-- src/Column/StringColumn.php | 4 +-- src/EntityRecordMapper.php | 10 +++--- src/EntityRecordRepository.php | 23 ++++++-------- src/RecordMapper.php | 10 ++---- src/RecordRepository.php | 13 ++++---- src/TableColumn.php | 44 ++++++++++++++------------ 15 files changed, 90 insertions(+), 87 deletions(-) delete mode 100644 .phpunit.cache/test-results diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results deleted file mode 100644 index 30d8bc5..0000000 --- a/.phpunit.cache/test-results +++ /dev/null @@ -1 +0,0 @@ -{"version":1,"defects":{"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_entry_then_get_its_plain_value":8,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_array_then_get_its_value":7,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_not_null_then_return_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_null_then_return_null":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_unset_null_then_return_null":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_entry_then_get_its_plain_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_array_then_get_its_value":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenPdoThenConfigurePdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenTheQueryAndItsParamsWhenExecuteIsCalledThenRunPrepareAndExecuteInPdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenStatementWhenExecIsCalledThenRunExecInPdo":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testBeginPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testCommitPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testRollbackPdoTransaction":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionFailsThenRollback":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionSucceedThenCommitAndReturnCallbackResponse":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\MysqlConnectionTest::testGivenPdoThenConfigurePdoWithMysqlInitCommand":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatements":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndRollbackGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":5,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testGivenNoTransactionThenWhenRollbackIsCalledThenThrowAnException":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_first_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_second_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_first_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_second_user":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::load_by_id":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update":5,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_first_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_second_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_first_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_second_address":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::insert":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by_query":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by_query":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_true":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_false":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::delete_where":5,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::update":5,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_when_value_is_null_then_get_null":8,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_one":7},"times":{"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\BoolColumnTest::given_an_array_then_get_its_value":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_not_null_then_return_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_null_then_return_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\ColumnTest::given_row_when_prop_is_unset_null_then_return_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_array_then_get_its_value":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\FloatColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\IntegerColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_entry_then_get_its_plain_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\StringColumnTest::given_an_array_then_get_its_value":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenPdoThenConfigurePdo":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenTheQueryAndItsParamsWhenExecuteIsCalledThenRunPrepareAndExecuteInPdo":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testGivenStatementWhenExecIsCalledThenRunExecInPdo":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testBeginPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testCommitPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testRollbackPdoTransaction":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionFailsThenRollback":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\GenericConnectionTest::testWhenTransactionSucceedThenCommitAndReturnCallbackResponse":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\MysqlConnectionTest::testGivenPdoThenConfigurePdoWithMysqlInitCommand":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenCommitGetsCalledThenRunBeginAndCommitStatements":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndCommitGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginThenRollbackGetsCalledThenRunBeginAndRollbackStatements":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testWhenBeginAndRollbackGetsCalledMultipleTimesThenRunStoreAndReleaseSavepoint":0,"Test\\Tcds\\Io\\Orm\\Unit\\Connection\\Pdo\\NestedTransactionConnectionTest::testGivenNoTransactionThenWhenRollbackIsCalledThenThrowAnException":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_first_user":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::get_plain_array_from_second_user":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_first_user":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordMapperTest::map_second_user":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::load_by_id":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete":0.002,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_first_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::get_plain_array_from_second_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_first_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordMapperTest::map_second_address":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::insert":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::load_by_query":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::list_by_query":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_true":0,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::exists_returns_false":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::delete_where":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\RecordRepositoryTest::update":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\DateTimeImmutableColumnTest::given_an_entry_when_value_is_null_then_get_null":0,"Test\\Tcds\\Io\\Orm\\Unit\\Column\\EnumColumnTest::given_an_entry_when_value_is_null_then_get_null":0.004,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update_one":0,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::update_many":0.001,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_one":0.009,"Test\\Tcds\\Io\\Orm\\Unit\\EntityRecordRepositoryTest::delete_many":0}} \ No newline at end of file diff --git a/src/Column/BoolColumn.php b/src/Column/BoolColumn.php index 6fc25a7..cc2b8bc 100644 --- a/src/Column/BoolColumn.php +++ b/src/Column/BoolColumn.php @@ -7,12 +7,12 @@ use Override; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class BoolColumn extends Column { - #[Override] public function plain($entry): bool + #[Override] public function plain($entry): ?bool { return parent::plain($entry); } diff --git a/src/Column/Column.php b/src/Column/Column.php index d2828c7..a99131b 100644 --- a/src/Column/Column.php +++ b/src/Column/Column.php @@ -8,12 +8,12 @@ /** * @template Entry - * @template Type + * @template Value */ abstract readonly class Column { /** - * @param Closure(Entry $record): Type $value + * @param Closure(Entry $record): Value $value */ public function __construct( public string $name, @@ -23,7 +23,7 @@ public function __construct( /** * @param Entry $entry - * @return Type|null + * @return Value|null */ public function plain($entry) { @@ -32,7 +32,7 @@ public function plain($entry) /** * @param array $row - * @return Type + * @return Value */ public function value(array $row) { @@ -41,7 +41,7 @@ public function value(array $row) /** * @param array $row - * @return Type|null + * @return Value|null */ public function nullable(array $row) { diff --git a/src/Column/DateColumn.php b/src/Column/DateColumn.php index d397ce4..3578417 100644 --- a/src/Column/DateColumn.php +++ b/src/Column/DateColumn.php @@ -9,8 +9,8 @@ use Override; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class DateColumn extends Column { @@ -23,6 +23,9 @@ #[Override] public function value(array $row): DateTime { - return new DateTime(parent::value($row)); + /** @var string $value */ + $value = parent::value($row); + + return new DateTime($value); } } diff --git a/src/Column/DateTimeColumn.php b/src/Column/DateTimeColumn.php index 21bd586..973d792 100644 --- a/src/Column/DateTimeColumn.php +++ b/src/Column/DateTimeColumn.php @@ -9,18 +9,21 @@ use Override; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class DateTimeColumn extends Column { - #[Override] public function plain($entry) + #[Override] public function plain($entry): ?string { return parent::plain($entry)?->format(DateTimeInterface::ATOM); } #[Override] public function value(array $row): DateTime { - return new DateTime(parent::value($row)); + /** @var string $value */ + $value = parent::value($row); + + return new DateTime($value); } } diff --git a/src/Column/DateTimeImmutableColumn.php b/src/Column/DateTimeImmutableColumn.php index d1745da..1e20556 100644 --- a/src/Column/DateTimeImmutableColumn.php +++ b/src/Column/DateTimeImmutableColumn.php @@ -9,8 +9,8 @@ use Override; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class DateTimeImmutableColumn extends Column { @@ -21,6 +21,9 @@ #[Override] public function value(array $row): DateTimeImmutable { - return new DateTimeImmutable(parent::value($row)); + /** @var string $value */ + $value = parent::value($row); + + return new DateTimeImmutable($value); } } diff --git a/src/Column/EnumColumn.php b/src/Column/EnumColumn.php index 2ab29ac..d81ca94 100644 --- a/src/Column/EnumColumn.php +++ b/src/Column/EnumColumn.php @@ -9,14 +9,14 @@ use Override; /** - * @template Entry of object - * @template Enum of class-string - * @extends Column + * @template EntryType + * @template ValueType of BackedEnum + * @extends Column */ readonly class EnumColumn extends Column { /** - * @param class-string $class + * @param class-string $class */ public function __construct( private string $class, @@ -26,17 +26,16 @@ public function __construct( parent::__construct($name, $value); } - #[Override] public function plain($entry): ?string + #[Override] public function plain($entry): string|int|null { return parent::plain($entry)?->value; } - /** - * @param array $row - * @return Enum - */ #[Override] public function value(array $row) { - return $this->class::from(parent::value($row)); + /** @var string $value */ + $value = parent::value($row); + + return $this->class::from($value); } } diff --git a/src/Column/FloatColumn.php b/src/Column/FloatColumn.php index 3a05778..8952b90 100644 --- a/src/Column/FloatColumn.php +++ b/src/Column/FloatColumn.php @@ -5,8 +5,8 @@ namespace Tcds\Io\Orm\Column; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class FloatColumn extends Column { diff --git a/src/Column/IntegerColumn.php b/src/Column/IntegerColumn.php index f0b23fe..e4e874e 100644 --- a/src/Column/IntegerColumn.php +++ b/src/Column/IntegerColumn.php @@ -5,8 +5,8 @@ namespace Tcds\Io\Orm\Column; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class IntegerColumn extends Column { diff --git a/src/Column/StringColumn.php b/src/Column/StringColumn.php index 57174d8..9fa8e8d 100644 --- a/src/Column/StringColumn.php +++ b/src/Column/StringColumn.php @@ -5,8 +5,8 @@ namespace Tcds\Io\Orm\Column; /** - * @template Entry of object - * @extends Column + * @template EntryType + * @extends Column */ readonly class StringColumn extends Column { diff --git a/src/EntityRecordMapper.php b/src/EntityRecordMapper.php index 02d8970..0fd8142 100644 --- a/src/EntityRecordMapper.php +++ b/src/EntityRecordMapper.php @@ -7,17 +7,17 @@ use Tcds\Io\Orm\Column\Column; /** - * @template T - * @template FK of int|string - * @extends RecordMapper + * @template EntryType + * @template ForeignKeyType + * @extends RecordMapper */ abstract class EntityRecordMapper extends RecordMapper { - /** @var Column */ + /** @var Column */ public readonly Column $primaryKey; /** - * @param Column $primaryKey + * @param Column $primaryKey */ public function __construct(Column $primaryKey) { diff --git a/src/EntityRecordRepository.php b/src/EntityRecordRepository.php index caada35..e009a93 100644 --- a/src/EntityRecordRepository.php +++ b/src/EntityRecordRepository.php @@ -7,17 +7,14 @@ use Tcds\Io\Orm\Connection\Connection; /** - * @template T - * @template FK - * @extends RecordMapper - * @internal + * @template EntryType + * @template ForeignKeyType + * @extends RecordRepository */ abstract class EntityRecordRepository extends RecordRepository { - /** - * @param EntityRecordMapper $entityMapper - */ public function __construct( + /** @var EntityRecordMapper */ protected EntityRecordMapper $entityMapper, Connection $connection, string $table, @@ -26,8 +23,8 @@ public function __construct( } /** - * @param FK $id - * @return T|null + * @param ForeignKeyType $id + * @return EntryType|null */ public function selectEntityById($id) { @@ -35,7 +32,7 @@ public function selectEntityById($id) } /** - * @param T $entity + * @param EntryType $entity */ public function updateOne($entity): void { @@ -46,7 +43,7 @@ public function updateOne($entity): void } /** - * @param T ...$entities + * @param EntryType ...$entities */ public function updateMany(...$entities): void { @@ -56,7 +53,7 @@ public function updateMany(...$entities): void } /** - * @param T $entity + * @param EntryType $entity */ public function deleteOne($entity): void { @@ -64,7 +61,7 @@ public function deleteOne($entity): void } /** - * @param T ...$entities + * @param EntryType ...$entities */ public function deleteMany(...$entities): void { diff --git a/src/RecordMapper.php b/src/RecordMapper.php index 54ad30a..5251565 100644 --- a/src/RecordMapper.php +++ b/src/RecordMapper.php @@ -8,16 +8,10 @@ /** * @template T - * @extends RecordRepository + * @extends TableColumn */ -abstract class RecordMapper +abstract class RecordMapper extends TableColumn { - /** @use TableColumn */ - use TableColumn; - - /** @var list> */ - public private(set) array $columns = []; - /** * @param array $row * @return T diff --git a/src/RecordRepository.php b/src/RecordRepository.php index 60188b9..31e1b5e 100644 --- a/src/RecordRepository.php +++ b/src/RecordRepository.php @@ -10,11 +10,12 @@ use Traversable; /** - * @template T + * @template EntryType */ abstract class RecordRepository { public function __construct( + /** @var RecordMapper */ protected readonly RecordMapper $mapper, protected readonly Connection $connection, protected readonly string $table, @@ -22,7 +23,7 @@ public function __construct( } /** - * @param T $entry + * @param EntryType $entry */ public function insertOne($entry): void { @@ -35,7 +36,7 @@ public function insertOne($entry): void /** * @param array $where - * @return T|null + * @return EntryType|null */ public function selectOneWhere(array $where) { @@ -51,7 +52,7 @@ public function selectOneWhere(array $where) /** * @param array $bindings - * @return T|null + * @return EntryType|null */ public function selectOneByQuery(string $selectQuery, array $bindings) { @@ -65,7 +66,7 @@ public function selectOneByQuery(string $selectQuery, array $bindings) /** * @param array $where - * @return Traversable + * @return Traversable */ public function selectManyWhere(array $where = [], ?int $limit = null, ?int $offset = null): Traversable { @@ -82,7 +83,7 @@ public function selectManyWhere(array $where = [], ?int $limit = null, ?int $off /** * @param array $bindings - * @return Traversable + * @return Traversable */ public function selectManyByQuery(string $selectQuery, array $bindings): Traversable { diff --git a/src/TableColumn.php b/src/TableColumn.php index 541dfc4..b25fad8 100644 --- a/src/TableColumn.php +++ b/src/TableColumn.php @@ -8,6 +8,7 @@ use Closure; use DateTimeInterface; use Tcds\Io\Orm\Column\BoolColumn; +use Tcds\Io\Orm\Column\Column; use Tcds\Io\Orm\Column\DateColumn; use Tcds\Io\Orm\Column\DateTimeColumn; use Tcds\Io\Orm\Column\DateTimeImmutableColumn; @@ -17,13 +18,16 @@ use Tcds\Io\Orm\Column\StringColumn; /** - * @template T + * @template EntryType */ -trait TableColumn +abstract class TableColumn { + /** @var list> */ + public private(set) array $columns = []; + /** - * @param Closure(T $entry): string $value - * @return StringColumn + * @param Closure(EntryType $entry): string $value + * @return StringColumn */ protected function string(string $name, Closure $value): StringColumn { @@ -34,8 +38,8 @@ protected function string(string $name, Closure $value): StringColumn } /** - * @param Closure(T $entry): bool $value - * @return BoolColumn + * @param Closure(EntryType $entry): bool $value + * @return BoolColumn */ protected function boolean(string $name, Closure $value): BoolColumn { @@ -46,8 +50,8 @@ protected function boolean(string $name, Closure $value): BoolColumn } /** - * @param Closure(T $entry): numeric $value - * @return FloatColumn + * @param Closure(EntryType $entry): float $value + * @return FloatColumn */ protected function numeric(string $name, Closure $value): FloatColumn { @@ -58,8 +62,8 @@ protected function numeric(string $name, Closure $value): FloatColumn } /** - * @param Closure(T $entry): int $value - * @return IntegerColumn + * @param Closure(EntryType $entry): int $value + * @return IntegerColumn */ protected function integer(string $name, Closure $value): IntegerColumn { @@ -70,8 +74,8 @@ protected function integer(string $name, Closure $value): IntegerColumn } /** - * @param Closure(T $entry): DateTimeInterface $value - * @return DateColumn + * @param Closure(EntryType $entry): DateTimeInterface $value + * @return DateColumn */ protected function date(string $name, Closure $value): DateColumn { @@ -82,8 +86,8 @@ protected function date(string $name, Closure $value): DateColumn } /** - * @param Closure(T $entry): DateTimeInterface $value - * @return DateTimeColumn + * @param Closure(EntryType $entry): DateTimeInterface $value + * @return DateTimeColumn */ protected function datetime(string $name, Closure $value): DateTimeColumn { @@ -94,8 +98,8 @@ protected function datetime(string $name, Closure $value): DateTimeColumn } /** - * @param Closure(T $entry): DateTimeInterface $value - * @return DateTimeImmutableColumn + * @param Closure(EntryType $entry): DateTimeInterface $value + * @return DateTimeImmutableColumn */ protected function datetimeImmutable(string $name, Closure $value): DateTimeImmutableColumn { @@ -106,10 +110,10 @@ protected function datetimeImmutable(string $name, Closure $value): DateTimeImmu } /** - * @template E of BackedEnum - * @param class-string $class - * @param Closure(T $entry): DateTimeInterface $value - * @return EnumColumn + * @template EnumType of BackedEnum + * @param class-string $class + * @param Closure(EntryType $entry): EnumType $value + * @return EnumColumn */ protected function enum(string $class, string $name, Closure $value): EnumColumn { From 9870d7d2d5453ea4a53e2d21e961592d64dc3c29 Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 11:54:02 +0200 Subject: [PATCH 5/8] Ignore phpunit cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c2fee4..d291940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .phpunit.result.cache +.phpunit.cache config/.phpcs-cache config/.phpunit.result.cache .idea/ From 067e1a3aa36f35a41b7b1d22343db1d9d6376272 Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 11:56:49 +0200 Subject: [PATCH 6/8] Fix cache --- .github/workflows/test-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index 6e7f0d0..fc02ebe 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -11,14 +11,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Validate composer.json and composer.lock run: composer validate - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: vendor key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} From b0a276e03db15d80f8a8a4dc61ebdc498b8bf1dc Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 11:58:28 +0200 Subject: [PATCH 7/8] Fix php version --- .github/workflows/test-deploy.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index fc02ebe..8f06fb2 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -11,6 +11,11 @@ jobs: runs-on: ubuntu-latest steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.4 + - uses: actions/checkout@v4 - name: Validate composer.json and composer.lock From 3790fe7ece9ba4a127cae4bbd4cef4a89902df22 Mon Sep 17 00:00:00 2001 From: Thiago Cordeiro Date: Tue, 13 May 2025 11:59:41 +0200 Subject: [PATCH 8/8] Remove duplicated mutation output --- .github/workflows/test-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-deploy.yml b/.github/workflows/test-deploy.yml index 8f06fb2..8dfe986 100644 --- a/.github/workflows/test-deploy.yml +++ b/.github/workflows/test-deploy.yml @@ -43,7 +43,7 @@ jobs: run: vendor/bin/phpunit --testdox --color=always - name: Mutation tests - run: vendor/bin/infection --threads=2 --min-msi=100 --ansi && cat var/log/infection.log + run: vendor/bin/infection --threads=2 --min-msi=100 --ansi - name: Mutation Report run: cat var/log/infection.log