diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index ad78810f..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: CI - -on: - push: - pull_request: - -jobs: - qa: - uses: php-db/phpdb-qa-tools/.github/workflows/continuous-integration.yml@0.1.x - with: - php-versions: '["8.2", "8.3", "8.4", "8.5"]' - run-integration: false diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 00000000..d11efbae --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,18 @@ +name: "Continuous Integration" + +on: + push: + pull_request: + +jobs: + qa: + uses: php-db/phpdb-qa-tools/.github/workflows/continuous-integration.yml@0.1.x + secrets: inherit + with: + php-versions: '["8.3", "8.4", "8.5"]' + run-integration: false + coverage-php-version: "8.4" + enable-codecov: true + enable-infection: true + min-msi: "80" + min-covered-msi: "80" diff --git a/.gitignore b/.gitignore index ecba80f1..aa2c7e67 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ /phpunit.xml /vendor/ /.vscode +/infection.json5 +/infection.log +/summary.log diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..8668e785 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,20 @@ +coverage: + status: + project: + default: + target: auto + threshold: 0% + base: auto + patch: + default: + target: auto + threshold: 0% + base: auto + +comment: + layout: "diff, flags, files" + behavior: default + require_changes: false + require_base: false + require_head: true + hide_project_coverage: false diff --git a/composer.json b/composer.json index eaa39168..9abfe5d4 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,10 @@ "config": { "sort-packages": true, "platform": { - "php": "8.2.99" + "php": "8.3.99" + }, + "allow-plugins": { + "infection/extension-installer": true } }, "extra": { @@ -26,11 +29,12 @@ } }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "laminas/laminas-servicemanager": "^3.0.0 || ^4.0.0", "laminas/laminas-stdlib": "^3.20.0" }, "require-dev": { + "infection/infection": "^0.34.1", "laminas/laminas-eventmanager": "^3.14.0", "laminas/laminas-hydrator": "^4.6.0", "php-db/phpdb-qa-tools": "0.1.x-dev", @@ -70,6 +74,7 @@ "mago lint --fix" ], "static-analysis": "mago analyze", + "mutation-test": "infection", "test": "phpunit --colors=always --testsuite \"unit test\"", "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", "test-integration": "phpunit --colors=always --testsuite \"integration test\"", diff --git a/composer.lock b/composer.lock index a4a79fbb..2fbf9dc1 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": "abc7c3577be8528422816192cc5068fa", + "content-hash": "6600948742b11dc28f0a4193e849e4d8", "packages": [ { "name": "brick/varexporter", @@ -57,16 +57,16 @@ }, { "name": "laminas/laminas-servicemanager", - "version": "4.5.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/laminas/laminas-servicemanager.git", - "reference": "a6996829c8ce55025cca1b57b1e8a8b165e3926c" + "reference": "11192d588876ad04ba2988984c77b4ecb5c771c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/a6996829c8ce55025cca1b57b1e8a8b165e3926c", - "reference": "a6996829c8ce55025cca1b57b1e8a8b165e3926c", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/11192d588876ad04ba2988984c77b4ecb5c771c2", + "reference": "11192d588876ad04ba2988984c77b4ecb5c771c2", "shasum": "" }, "require": { @@ -130,7 +130,7 @@ "chat": "https://laminas.dev/chat", "forum": "https://discourse.laminas.dev", "issues": "https://github.com/laminas/laminas-servicemanager/issues", - "source": "https://github.com/laminas/laminas-servicemanager/tree/4.5.0" + "source": "https://github.com/laminas/laminas-servicemanager/tree/4.5.1" }, "funding": [ { @@ -138,7 +138,7 @@ "type": "community_bridge" } ], - "time": "2025-10-14T09:41:04+00:00" + "time": "2026-05-12T09:53:32+00:00" }, { "name": "laminas/laminas-stdlib", @@ -201,20 +201,19 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.8.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -253,9 +252,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-07-04T14:30:18+00:00" }, { "name": "psr/container", @@ -312,6 +311,236 @@ } ], "packages-dev": [ + { + "name": "colinodell/json5", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/colinodell/json5.git", + "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinodell/json5/zipball/5724d21bc5c910c2560af1b8915f0cc0163579c8", + "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0" + }, + "require-dev": { + "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" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "files": [ + "src/global.php" + ], + "psr-4": { + "ColinODell\\Json5\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Developer" + } + ], + "description": "UTF-8 compatible JSON5 parser for PHP", + "homepage": "https://github.com/colinodell/json5", + "keywords": [ + "JSON5", + "json", + "json5_decode", + "json_decode" + ], + "support": { + "issues": "https://github.com/colinodell/json5/issues", + "source": "https://github.com/colinodell/json5/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://www.patreon.com/colinodell", + "type": "patreon" + } + ], + "time": "2024-02-09T13:06:12+00:00" + }, + { + "name": "composer/pcre", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-06-07T11:47:49+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.5", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "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.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, { "name": "doctrine/annotations", "version": "2.0.2", @@ -466,6 +695,501 @@ ], "time": "2024-02-05T11:56:58+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" + }, + { + "name": "infection/abstract-testframework-adapter", + "version": "0.5.0", + "source": { + "type": "git", + "url": "https://github.com/infection/abstract-testframework-adapter.git", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^2.17", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\AbstractTestFramework\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Abstract Test Framework Adapter for Infection", + "support": { + "issues": "https://github.com/infection/abstract-testframework-adapter/issues", + "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.5.0" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-17T18:49:12+00:00" + }, + { + "name": "infection/extension-installer", + "version": "0.1.2", + "source": { + "type": "git", + "url": "https://github.com/infection/extension-installer.git", + "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/extension-installer/zipball/9b351d2910b9a23ab4815542e93d541e0ca0cdcf", + "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.9 || ^2.0", + "friendsofphp/php-cs-fixer": "^2.18, <2.19", + "infection/infection": "^0.15.2", + "php-coveralls/php-coveralls": "^2.4", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.10", + "phpstan/phpstan-phpunit": "^0.12.6", + "phpstan/phpstan-strict-rules": "^0.12.2", + "phpstan/phpstan-webmozart-assert": "^0.12.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.8" + }, + "type": "composer-plugin", + "extra": { + "class": "Infection\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Infection\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Infection Extension Installer", + "support": { + "issues": "https://github.com/infection/extension-installer/issues", + "source": "https://github.com/infection/extension-installer/tree/0.1.2" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-10-20T22:08:34+00:00" + }, + { + "name": "infection/include-interceptor", + "version": "0.2.5", + "source": { + "type": "git", + "url": "https://github.com/infection/include-interceptor.git", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", + "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "infection/infection": "^0.15.0", + "phan/phan": "^2.4 || ^3", + "php-coveralls/php-coveralls": "^2.2", + "phpstan/phpstan": "^0.12.8", + "phpunit/phpunit": "^8.5", + "vimeo/psalm": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Infection\\StreamWrapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com" + } + ], + "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", + "support": { + "issues": "https://github.com/infection/include-interceptor/issues", + "source": "https://github.com/infection/include-interceptor/tree/0.2.5" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2021-08-09T10:03:57+00:00" + }, + { + "name": "infection/infection", + "version": "0.34.2", + "source": { + "type": "git", + "url": "https://github.com/infection/infection.git", + "reference": "18d9bef39fae250f202920d1453ab96e793e6637" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/infection/infection/zipball/18d9bef39fae250f202920d1453ab96e793e6637", + "reference": "18d9bef39fae250f202920d1453ab96e793e6637", + "shasum": "" + }, + "require": { + "colinodell/json5": "^3.0", + "composer-runtime-api": "^2.0", + "composer/xdebug-handler": "^3.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "fidry/cpu-core-counter": "^1.0", + "infection/abstract-testframework-adapter": "^0.5.0", + "infection/extension-installer": "^0.1.0", + "infection/include-interceptor": "^0.2.5 || ^1.0.0", + "infection/mutator": "^0.4", + "justinrainbow/json-schema": "^6.0", + "nikic/php-parser": "^5.6.2", + "ondram/ci-detector": "^4.1.0", + "php": "^8.3", + "psr/log": "^2.0 || ^3.0", + "sanmai/di-container": "^0.1.16", + "sanmai/duoclock": "^0.1.0", + "sanmai/later": "^0.1.7", + "sanmai/pipeline": "^7.2", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "symfony/console": "^6.4 || ^7.4 || ^8.0", + "symfony/filesystem": "^6.4 || ^7.4 || ^8.0", + "symfony/finder": "^6.4 || ^7.4 || ^8.0", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^6.4 || ^7.4 || ^8.0", + "thecodingmachine/safe": "^v3.0", + "webmozart/assert": "^1.11 || ^2.0" + }, + "conflict": { + "antecedent/patchwork": "<2.1.25", + "dg/bypass-finals": "<1.4.1" + }, + "require-dev": { + "carthage-software/mago": "^1.20", + "ext-simplexml": "*", + "fidry/makefile": "^1.0", + "fig/log-test": "^1.2", + "phpat/phpat": "^0.12.4", + "phpbench/phpbench": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-webmozart-assert": "^2.0", + "phpunit/phpunit": "^12.5.29", + "rector/rector": "^2.2.4", + "shipmonk/dead-code-detector": "^1.3", + "shipmonk/name-collision-detector": "^2.1", + "sidz/phpstan-rules": "^0.5.1", + "symfony/yaml": "^6.4 || ^7.4 || ^8.0", + "thecodingmachine/phpstan-safe-rule": "^1.4", + "webmozarts/strict-phpunit": "^7.15" + }, + "bin": [ + "bin/infection" + ], + "type": "library", + "autoload": { + "psr-4": { + "Infection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Maks Rafalko", + "email": "maks.rafalko@gmail.com", + "homepage": "https://twitter.com/maks_rafalko" + }, + { + "name": "Oleg Zhulnev", + "homepage": "https://github.com/sidz" + }, + { + "name": "Gert de Pagter", + "homepage": "https://github.com/BackEndTea" + }, + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com", + "homepage": "https://twitter.com/tfidry" + }, + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "homepage": "https://www.alexeykopytko.com" + }, + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.", + "keywords": [ + "coverage", + "mutant", + "mutation framework", + "mutation testing", + "testing", + "unit testing" + ], + "support": { + "issues": "https://github.com/infection/infection/issues", + "source": "https://github.com/infection/infection/tree/0.34.2" + }, + "funding": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2026-08-07T12:59:47+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": [ + { + "url": "https://github.com/infection", + "type": "github" + }, + { + "url": "https://opencollective.com/infection", + "type": "open_collective" + } + ], + "time": "2025-04-29T08:19:52+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "6.11.0", + "source": { + "type": "git", + "url": "https://github.com/jsonrainbow/json-schema.git", + "reference": "7e420a943a6fbc95e60e3cf67acfbee85b3b4da7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/7e420a943a6fbc95e60e3cf67acfbee85b3b4da7", + "reference": "7e420a943a6fbc95e60e3cf67acfbee85b3b4da7", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-json": "*", + "marc-mabe/php-enum": "^4.4", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.3.0", + "json-schema/json-schema-test-suite": "dev-main", + "marc-mabe/php-enum-phpstan": "^2.0", + "phpspec/prophecy": "^1.19", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^8.5" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Danny van der Sluijs", + "email": "danny.vandersluijs@icloud.com", + "role": "Maintainer" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/jsonrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/jsonrainbow/json-schema/issues", + "source": "https://github.com/jsonrainbow/json-schema/tree/6.11.0" + }, + "time": "2026-08-21T10:30:42+00:00" + }, { "name": "laminas/laminas-eventmanager", "version": "3.15.0", @@ -539,16 +1263,16 @@ }, { "name": "laminas/laminas-hydrator", - "version": "4.18.0", + "version": "4.19.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-hydrator.git", - "reference": "ab208d1b8a2dc4251182a6cd2d123ccbc56eda50" + "reference": "bf6c980013f70b1141aadef979cf8b66241fff4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/ab208d1b8a2dc4251182a6cd2d123ccbc56eda50", - "reference": "ab208d1b8a2dc4251182a6cd2d123ccbc56eda50", + "url": "https://api.github.com/repos/laminas/laminas-hydrator/zipball/bf6c980013f70b1141aadef979cf8b66241fff4f", + "reference": "bf6c980013f70b1141aadef979cf8b66241fff4f", "shasum": "" }, "require": { @@ -612,24 +1336,97 @@ "type": "community_bridge" } ], - "time": "2026-01-13T10:10:41+00:00" + "time": "2026-05-13T22:31:51+00:00" + }, + { + "name": "marc-mabe/php-enum", + "version": "v4.7.2", + "source": { + "type": "git", + "url": "https://github.com/marc-mabe/php-enum.git", + "reference": "bb426fcdd65c60fb3638ef741e8782508fda7eef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marc-mabe/php-enum/zipball/bb426fcdd65c60fb3638ef741e8782508fda7eef", + "reference": "bb426fcdd65c60fb3638ef741e8782508fda7eef", + "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.2" + }, + "time": "2025-09-14T11:18:39+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.13.4", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", + "reference": "8680aa248f8e07bc8fb43f56f0f5fc77a0c96aae", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.0" }, "conflict": { "doctrine/collections": "<1.6.8", @@ -664,15 +1461,93 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "source": "https://github.com/myclabs/DeepCopy/tree/1.14.0" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" + "url": "https://github.com/mnapoli", + "type": "github" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2026-08-11T10:17:44+00:00" + }, + { + "name": "ondram/ci-detector", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/OndraM/ci-detector.git", + "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", + "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.13.2", + "lmc/coding-standard": "^3.0.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "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": { + "psr-4": { + "OndraM\\CiDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Machulda", + "email": "ondrej.machulda@gmail.com" + } + ], + "description": "Detect continuous integration environment and provide unified access to properties of current build", + "keywords": [ + "CircleCI", + "Codeship", + "Wercker", + "adapter", + "appveyor", + "aws", + "aws codebuild", + "azure", + "azure devops", + "azure pipelines", + "bamboo", + "bitbucket", + "buddy", + "ci-info", + "codebuild", + "continuous integration", + "continuousphp", + "devops", + "drone", + "github", + "gitlab", + "interface", + "jenkins", + "pipelines", + "sourcehut", + "teamcity", + "travis" + ], + "support": { + "issues": "https://github.com/OndraM/ci-detector/issues", + "source": "https://github.com/OndraM/ci-detector/tree/4.2.0" + }, + "time": "2024-03-12T13:22:30+00:00" }, { "name": "phar-io/manifest", @@ -889,16 +1764,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.3", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "b641dde59d969ea42eed70a39f9b51950bc96878" + "reference": "3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/b641dde59d969ea42eed70a39f9b51950bc96878", - "reference": "b641dde59d969ea42eed70a39f9b51950bc96878", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe", + "reference": "3d13c0d5dcf8730a67b70fa7fb03b4556b5cc0fe", "shasum": "" }, "require": { @@ -909,7 +1784,7 @@ "ext-reflection": "*", "ext-spl": "*", "ext-tokenizer": "*", - "php": "^8.1", + "php": "^8.2", "phpbench/container": "^2.2", "psr/log": "^1.1 || ^2.0 || ^3.0", "seld/jsonlint": "^1.1", @@ -923,14 +1798,15 @@ "require-dev": { "dantleech/invoke": "^2.0", "ergebnis/composer-normalize": "^2.39", - "jangregor/phpstan-prophecy": "^1.0", + "jangregor/phpstan-prophecy": "^2.0", "php-cs-fixer/shim": "^3.9", "phpspec/prophecy": "^1.22", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.4 || ^11.0", - "rector/rector": "^1.2", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^11.5", + "rector/rector": "^2.2", + "sebastian/exporter": "^6.3.2", "symfony/error-handler": "^6.1 || ^7.0 || ^8.0", "symfony/var-dumper": "^6.1 || ^7.0 || ^8.0" }, @@ -975,7 +1851,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.3" + "source": "https://github.com/phpbench/phpbench/tree/1.7.0" }, "funding": [ { @@ -983,15 +1859,15 @@ "type": "github" } ], - "time": "2025-11-06T19:07:31+00:00" + "time": "2026-06-08T19:09:20+00:00" }, { "name": "phpstan/phpstan", - "version": "2.2.7", + "version": "2.2.9", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/692db47b9dddb0487934e5236e77d48594aef921", - "reference": "692db47b9dddb0487934e5236e77d48594aef921", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/13d6b4f347bad222da436580c8304fa6f83e6bd0", + "reference": "13d6b4f347bad222da436580c8304fa6f83e6bd0", "shasum": "" }, "require": { @@ -1047,7 +1923,7 @@ "type": "github" } ], - "time": "2026-07-29T17:39:32+00:00" + "time": "2026-08-22T07:38:16+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1141,28 +2017,28 @@ }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -1190,15 +2066,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.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-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", @@ -1386,42 +2274,43 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.46", + "version": "11.5.56", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "75dfe79a2aa30085b7132bb84377c24062193f33" + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/75dfe79a2aa30085b7132bb84377c24062193f33", - "reference": "75dfe79a2aa30085b7132bb84377c24062193f33", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f83edffa6967c3db468d48a695ec7bcb02e9256", + "reference": "5f83edffa6967c3db468d48a695ec7bcb02e9256", "shasum": "" }, "require": { "ext-dom": "*", + "ext-filter": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.11", - "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.2", + "sebastian/comparator": "^6.3.3", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.1", "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", + "sebastian/recursion-context": "^6.0.3", "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" @@ -1439,12 +2328,291 @@ } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.56" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-07-06T14:52:39+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "rector/rector", + "version": "2.6.3", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "7e46709996a4b3dc59e1d6ecbb6a38ace335bd58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/7e46709996a4b3dc59e1d6ecbb6a38ace335bd58", + "reference": "7e46709996a4b3dc59e1d6ecbb6a38ace335bd58", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.2.6" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "homepage": "https://getrector.com/", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.6.3" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2026-08-18T22:01:18+00:00" + }, + { + "name": "sanmai/di-container", + "version": "0.1.23", + "source": { + "type": "git", + "url": "https://github.com/sanmai/di-container.git", + "reference": "8cf59c091f33297389d0a5a27ea0d688df15c376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sanmai/di-container/zipball/8cf59c091f33297389d0a5a27ea0d688df15c376", + "reference": "8cf59c091f33297389d0a5a27ea0d688df15c376", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/container": "^1.1.2 || ^2.0", + "sanmai/pipeline": "^7.10" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3.17", + "infection/infection": ">=0.31", + "php-coveralls/php-coveralls": "^2.4.1", + "phpbench/phpbench": "^1.4", + "phpstan/extension-installer": "^1.4", + "phpunit/phpunit": "^11.5.25", + "sanmai/phpstan-rules": "^0.3.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + }, + "preferred-install": "dist" + }, + "autoload": { + "psr-4": { + "DIContainer\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1452,205 +2620,232 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com", + "homepage": "https://github.com/sanmai" + }, + { + "name": "Maks Rafalko", + "homepage": "https://twitter.com/maks_rafalko" + }, + { + "name": "Théo FIDRY", + "homepage": "https://twitter.com/tfidry" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "dependency injection container with automatic constructor dependency resolution", "keywords": [ - "phpunit", - "testing", - "xunit" + "Autowiring", + "constructor di", + "di container", + "psr 11" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.46" + "issues": "https://github.com/sanmai/di-container/issues", + "source": "https://github.com/sanmai/di-container/tree/0.1.23" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/sanmai", "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": "2025-12-06T08:01:15+00:00" + "time": "2026-08-11T00:58:41+00:00" }, { - "name": "psr/cache", - "version": "3.0.0", + "name": "sanmai/duoclock", + "version": "0.1.3", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + "url": "https://github.com/sanmai/DuoClock.git", + "reference": "47461e3ff65b7308635047831a55615652e7be1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "url": "https://api.github.com/repos/sanmai/DuoClock/zipball/47461e3ff65b7308635047831a55615652e7be1a", + "reference": "47461e3ff65b7308635047831a55615652e7be1a", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.2", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "friendsofphp/php-cs-fixer": "^3.17", + "infection/infection": ">=0.29", + "php-coveralls/php-coveralls": "^2.4.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^11.5.25", + "sanmai/phpstan-rules": "^0.3.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "preferred-install": "dist" }, "autoload": { "psr-4": { - "Psr\\Cache\\": "src/" + "DuoClock\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" } ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], + "description": "PHP time mocking for tests - PSR-20 clock with mockable sleep(), time(), and TimeSpy for PHPUnit testing", "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "issues": "https://github.com/sanmai/DuoClock/issues", + "source": "https://github.com/sanmai/DuoClock/tree/0.1.3" }, - "time": "2021-02-03T23:26:27+00:00" + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2025-12-26T06:12:34+00:00" }, { - "name": "psr/log", - "version": "3.0.2", + "name": "sanmai/later", + "version": "0.1.8", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "url": "https://github.com/sanmai/later.git", + "reference": "c56aeb8fa7fdf81eda2640a68b51884685963d13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/sanmai/later/zipball/c56aeb8fa7fdf81eda2640a68b51884685963d13", + "reference": "c56aeb8fa7fdf81eda2640a68b51884685963d13", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.2" + }, + "require-dev": { + "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": ">=1.4.5", + "phpunit/phpunit": ">=9.5 <10", + "vimeo/psalm": ">=2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-main": "0.1.x-dev" } }, "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Psr\\Log\\": "src" + "Later\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], + "description": "Later: deferred wrapper object", "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" + "issues": "https://github.com/sanmai/later/issues", + "source": "https://github.com/sanmai/later/tree/0.1.8" }, - "time": "2024-09-11T13:17:53+00:00" + "funding": [ + { + "url": "https://github.com/sanmai", + "type": "github" + } + ], + "time": "2026-06-29T07:24:33+00:00" }, { - "name": "rector/rector", - "version": "2.3.1", + "name": "sanmai/pipeline", + "version": "7.10", "source": { "type": "git", - "url": "https://github.com/rectorphp/rector.git", - "reference": "9afc1bb43571b25629f353c61a9315b5ef31383a" + "url": "https://github.com/sanmai/pipeline.git", + "reference": "a8e4e57a6031efc7a92defd13fed9c1c4c73fc71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/9afc1bb43571b25629f353c61a9315b5ef31383a", - "reference": "9afc1bb43571b25629f353c61a9315b5ef31383a", + "url": "https://api.github.com/repos/sanmai/pipeline/zipball/a8e4e57a6031efc7a92defd13fed9c1c4c73fc71", + "reference": "a8e4e57a6031efc7a92defd13fed9c1c4c73fc71", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "phpstan/phpstan": "^2.1.33" - }, - "conflict": { - "rector/rector-doctrine": "*", - "rector/rector-downgrade-php": "*", - "rector/rector-phpunit": "*", - "rector/rector-symfony": "*" + "php": ">=8.2" }, - "suggest": { - "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + "require-dev": { + "ergebnis/composer-normalize": "^2.8", + "esi/phpunit-coverage-check": ">2", + "friendsofphp/php-cs-fixer": "^3.17", + "infection/infection": ">=0.32.3", + "league/pipeline": "^0.3 || ^1.0", + "php-coveralls/php-coveralls": "^2.4.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^11 || ^12", + "sanmai/phpstan-rules": "^0.3.11", + "sanmai/phpunit-double-colon-syntax": "^0.1.1", + "vimeo/psalm": ">=2" }, - "bin": [ - "bin/rector" - ], "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.x-dev" + } + }, "autoload": { "files": [ - "bootstrap.php" - ] + "src/functions.php" + ], + "psr-4": { + "Pipeline\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "Apache-2.0" ], - "description": "Instant Upgrade and Automated Refactoring of any PHP code", - "homepage": "https://getrector.com/", - "keywords": [ - "automation", - "dev", - "migration", - "refactoring" + "authors": [ + { + "name": "Alexey Kopytko", + "email": "alexey@kopytko.com" + } ], + "description": "General-purpose collections pipeline", "support": { - "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/2.3.1" + "issues": "https://github.com/sanmai/pipeline/issues", + "source": "https://github.com/sanmai/pipeline/tree/7.10" }, "funding": [ { - "url": "https://github.com/tomasvotruba", + "url": "https://github.com/sanmai", "type": "github" } ], - "time": "2026-01-13T15:13:58+00:00" + "time": "2026-08-03T04:56:22+00:00" }, { "name": "sebastian/cli-parser", @@ -1824,16 +3019,16 @@ }, { "name": "sebastian/comparator", - "version": "6.3.2", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { @@ -1892,7 +3087,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { @@ -1912,7 +3107,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T08:07:46+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { "name": "sebastian/complexity", @@ -2640,16 +3835,16 @@ }, { "name": "seld/jsonlint", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + "reference": "9a90eb5d32d5a500296bf43f946d60246444d5f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9a90eb5d32d5a500296bf43f946d60246444d5f7", + "reference": "9a90eb5d32d5a500296bf43f946d60246444d5f7", "shasum": "" }, "require": { @@ -2688,7 +3883,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + "source": "https://github.com/Seldaek/jsonlint/tree/1.12.1" }, "funding": [ { @@ -2700,7 +3895,7 @@ "type": "tidelift" } ], - "time": "2024-07-11T14:55:45+00:00" + "time": "2026-06-12T11:32:29+00:00" }, { "name": "staabm/side-effects-detector", @@ -2756,16 +3951,16 @@ }, { "name": "symfony/console", - "version": "v7.4.3", + "version": "v7.4.17", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6" + "reference": "962e18f09ebe68a49039b4c82fc0ea4871824fca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/732a9ca6cd9dfd940c639062d5edbde2f6727fb6", - "reference": "732a9ca6cd9dfd940c639062d5edbde2f6727fb6", + "url": "https://api.github.com/repos/symfony/console/zipball/962e18f09ebe68a49039b4c82fc0ea4871824fca", + "reference": "962e18f09ebe68a49039b4c82fc0ea4871824fca", "shasum": "" }, "require": { @@ -2830,7 +4025,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.3" + "source": "https://github.com/symfony/console/tree/v7.4.17" }, "funding": [ { @@ -2850,20 +4045,20 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-08-21T12:09:28+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -2876,7 +4071,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -2901,7 +4096,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.6.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -2912,25 +4107,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/filesystem", - "version": "v7.4.0", + "version": "v7.4.17", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a" + "reference": "ee7bc7bca4c7079b88e57d5000aeeb20df570c8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d551b38811096d0be9c4691d406991b47c0c630a", - "reference": "d551b38811096d0be9c4691d406991b47c0c630a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ee7bc7bca4c7079b88e57d5000aeeb20df570c8d", + "reference": "ee7bc7bca4c7079b88e57d5000aeeb20df570c8d", "shasum": "" }, "require": { @@ -2967,7 +4166,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.4.0" + "source": "https://github.com/symfony/filesystem/tree/v7.4.17" }, "funding": [ { @@ -2987,20 +4186,20 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-08-21T12:09:28+00:00" }, { "name": "symfony/finder", - "version": "v7.4.3", + "version": "v7.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06" + "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/fffe05569336549b20a1be64250b40516d6e8d06", - "reference": "fffe05569336549b20a1be64250b40516d6e8d06", + "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", "shasum": "" }, "require": { @@ -3035,7 +4234,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.3" + "source": "https://github.com/symfony/finder/tree/v7.4.17" }, "funding": [ { @@ -3055,20 +4254,20 @@ "type": "tidelift" } ], - "time": "2025-12-23T14:50:43+00:00" + "time": "2026-08-21T12:09:28+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.4.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "b38026df55197f9e39a44f3215788edf83187b80" + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/b38026df55197f9e39a44f3215788edf83187b80", - "reference": "b38026df55197f9e39a44f3215788edf83187b80", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", + "reference": "2888fcdc4dc2fd5f7c7397be78631e8af12e02b4", "shasum": "" }, "require": { @@ -3106,7 +4305,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.4.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.4.8" }, "funding": [ { @@ -3126,20 +4325,20 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.33.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -3189,7 +4388,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -3209,20 +4408,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", "shasum": "" }, "require": { @@ -3271,7 +4470,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" }, "funding": [ { @@ -3291,20 +4490,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T09:58:17+00:00" + "time": "2026-07-28T08:25:59+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { @@ -3356,7 +4555,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -3376,20 +4575,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { @@ -3441,7 +4640,87 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-27T06:59:30+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.41.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/255fab485aaa1006ed411040c42aecd7b5302d7a", + "reference": "255fab485aaa1006ed411040c42aecd7b5302d7a", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.41.0" }, "funding": [ { @@ -3461,20 +4740,20 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2026-07-01T12:47:55+00:00" }, { "name": "symfony/process", - "version": "v7.4.3", + "version": "v7.4.17", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f" + "reference": "058d17fc284cce14efb2385783b55014a461b176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2f8e1a6cdf590ca63715da4d3a7a3327404a523f", - "reference": "2f8e1a6cdf590ca63715da4d3a7a3327404a523f", + "url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176", + "reference": "058d17fc284cce14efb2385783b55014a461b176", "shasum": "" }, "require": { @@ -3506,7 +4785,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.3" + "source": "https://github.com/symfony/process/tree/v7.4.17" }, "funding": [ { @@ -3526,20 +4805,20 @@ "type": "tidelift" } ], - "time": "2025-12-19T10:00:43+00:00" + "time": "2026-08-21T17:40:08+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.1", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/c0a284bab1ed8aa0417e3d69250ab437739563a0", + "reference": "c0a284bab1ed8aa0417e3d69250ab437739563a0", "shasum": "" }, "require": { @@ -3557,7 +4836,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.7-dev" } }, "autoload": { @@ -3593,7 +4872,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.1" }, "funding": [ { @@ -3613,20 +4892,20 @@ "type": "tidelift" } ], - "time": "2025-07-15T11:30:57+00:00" + "time": "2026-06-16T09:55:08+00:00" }, { "name": "symfony/string", - "version": "v7.4.0", + "version": "v7.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" + "reference": "e394af32256bf9e7bf80849d95e589167c10097b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", - "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", + "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", + "reference": "e394af32256bf9e7bf80849d95e589167c10097b", "shasum": "" }, "require": { @@ -3684,7 +4963,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.0" + "source": "https://github.com/symfony/string/tree/v7.4.15" }, "funding": [ { @@ -3704,7 +4983,150 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2026-07-28T07:33:02+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00" }, { "name": "theseer/tokenizer", @@ -3758,16 +5180,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.2", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", "shasum": "" }, "require": { @@ -3783,7 +5205,11 @@ }, "type": "library", "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, "branch-alias": { + "dev-master": "2.0-dev", "dev-feature/2-0": "2.0-dev" } }, @@ -3814,9 +5240,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.2" + "source": "https://github.com/webmozarts/assert/tree/2.4.1" }, - "time": "2026-01-13T14:02:24+00:00" + "time": "2026-06-15T15:31:57+00:00" }, { "name": "webmozart/glob", @@ -3876,11 +5302,11 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "platform-dev": {}, "platform-overrides": { - "php": "8.2.99" + "php": "8.3.99" }, "plugin-api-version": "2.9.0" } diff --git a/docs/book/docker-deployment.md b/docs/book/docker-deployment.md index 721d2749..dc8bd45c 100644 --- a/docs/book/docker-deployment.md +++ b/docs/book/docker-deployment.md @@ -13,7 +13,7 @@ Two web server options are supported: **Nginx with PHP-FPM** Create a `Dockerfile` in your project root: ```dockerfile -FROM php:8.2-fpm-alpine +FROM php:8.3-fpm-alpine RUN apk add --no-cache git zip unzip \ && docker-php-ext-install pdo_mysql @@ -55,7 +55,7 @@ server { Create a `Dockerfile` in your project root: ```dockerfile -FROM php:8.2-apache +FROM php:8.3-apache RUN apt-get update \ && apt-get install -y git zlib1g-dev libzip-dev \ diff --git a/docs/book/sql-ddl/alter-drop.md b/docs/book/sql-ddl/alter-drop.md index c0105350..bd2bd895 100644 --- a/docs/book/sql-ddl/alter-drop.md +++ b/docs/book/sql-ddl/alter-drop.md @@ -430,6 +430,68 @@ $fk = new ForeignKey( ); ``` +### Table Prefixes + +`TableIdentifier` accepts an optional prefix and separator. When a prefix is +set, `getTable()` and `getTableAndSchema()` return the prefixed table name; +`getUnprefixedTable()` returns the table name as given: + +```php title="Prefixed Table Identifiers" +use PhpDb\Sql\TableIdentifier; + +$identifier = new TableIdentifier('users', null, 'backup'); +$identifier->getTable(); // 'backup_users' +$identifier->getUnprefixedTable(); // 'users' + +// Custom separator +$identifier = new TableIdentifier('users', null, 'backup', '__'); +$identifier->getTable(); // 'backup__users' +``` + +The prefix must be a non-empty string or `null`. The separator defaults to +`'_'` and must also be a non-empty string; passing `''` for either throws +`PhpDb\Sql\Exception\InvalidArgumentException`. + +### The TableIdentifierFactory + +`PhpDb\Sql\TableIdentifierFactory` is a callable factory that creates +`TableIdentifier` instances sharing a preconfigured prefix and separator: + +```php title="Creating Identifiers via the Factory" +use PhpDb\Sql\TableIdentifierFactory; + +$factory = new TableIdentifierFactory('backup'); + +$factory('users')->getTable(); // 'backup_users' +$factory('orders', 'sales')->getTable(); // 'backup_orders' + +// A prefix or separator passed at call time overrides the configured one +$factory('users', null, 'archive')->getTable(); // 'archive_users' +``` + +The configured separator is only applied when a prefix is passed, so a factory +created with a separator but no prefix produces unprefixed identifiers. Passing +`null` as the separator falls back to the default `'_'`; passing `''` — at +construction or at call time — throws +`PhpDb\Sql\Exception\InvalidArgumentException`. + +`PhpDb\ConfigProvider` registers the factory as a container service. Configure +the prefix and separator through the application config: + +```php title="Configuring the Factory Service" +use PhpDb\Sql\TableIdentifierFactory; + +return [ + TableIdentifierFactory::class => [ + 'prefix' => 'backup', + 'separator' => '_', + ], +]; +``` + +When no configuration is present the service is created without a prefix, and +the separator defaults to `'_'`. + ## Nullable and Default Values ### Setting Nullable diff --git a/infection.json5.dist b/infection.json5.dist new file mode 100644 index 00000000..71b82aa6 --- /dev/null +++ b/infection.json5.dist @@ -0,0 +1,22 @@ +{ + "$schema": "vendor/infection/infection/resources/schema.json", + "source": { + "directories": [ + "src" + ] + }, + "timeout": 10, + "threads": "max", + "logs": { + "text": "infection.log", + "summary": "summary.log", + "stryker": { + // matches versioned release branches, e.g. "0.1.x", "0.2.x" + "badge": "/^\\d+\\.\\d+\\.x$/" + } + }, + "mutators": { + "@default": true + }, + "staticAnalysisTool": "mago" +} diff --git a/lint-baseline.toml b/lint-baseline.toml index d62c7073..99046f37 100644 --- a/lint-baseline.toml +++ b/lint-baseline.toml @@ -2808,6 +2808,66 @@ code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeThrowsWhenContainerHasNoConfig`." count = 1 +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 9 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithConfiguredPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithConfiguredSeparatorWithoutPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithConfiguredSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithoutPrefixWhenConfigIsEmpty`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithoutPrefixWhenConfigServiceIsNull`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithoutPrefixWhenContainerHasNoConfig`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeCreatesFactoryWithoutPrefixWhenPrefixKeyIsAbsent`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeRejectsEmptyStringSeparatorFromConfig`." +count = 1 + +[[issues]] +file = "test/unit/Container/TableIdentifierFactoryFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeUsesDefaultSeparatorWhenSeparatorKeyIsAbsent`." +count = 1 + [[issues]] file = "test/unit/Exception/ContainerExceptionTest.php" code = "assertion-style" @@ -7668,11 +7728,179 @@ code = "strict-assertions" message = "Use strict assertions in PHPUnit tests." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 25 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `''` should be passed as a named argument for clarity." +count = 2 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `'_'` should be passed as a named argument for clarity." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `'__'` should be passed as a named argument for clarity." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `'archive'` should be passed as a named argument for clarity." +count = 3 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `'public'` should be passed as a named argument for clarity." +count = 2 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `null` should be passed as a named argument for clarity." +count = 8 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallTimePrefixIsAppliedWhenNonePreconfigured`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallTimePrefixOverridesConfiguredPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallTimeSeparatorIsAppliedWhenNonePreconfigured`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallTimeSeparatorOverridesConfiguredSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConfiguredSeparatorIsCarriedButUnusedWhenNoPrefixApplies`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCreatesIdentifierWithConfiguredPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCreatesIdentifierWithConfiguredSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCreatesIdentifierWithSchema`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCreatesIdentifierWithoutPrefixWhenNoneConfigured`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testPrefixIsNullByDefault`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringCallTimePrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringCallTimeSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringSeparatorWithoutPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testReturnsConfiguredPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testReturnsConfiguredSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSeparatorDefaultsToUnderscoreWhenNoPrefixConfigured`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSeparatorDefaultsToUnderscore`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSeparatorFallsBackToDefaultWhenPassedAsNull`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "assertion-style" message = "Inconsistent assertions style." -count = 7 +count = 17 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDefaultPrefix`." +count = 1 [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" @@ -7680,6 +7908,18 @@ code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDefaultSchema`." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDefaultSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetPrefix`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "prefer-test-attribute" @@ -7692,24 +7932,84 @@ code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetSchema`." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableAndSchemaAppliesPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableAndSchemaWithoutPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableAppliesPrefixWithCustomSeparator`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableAppliesPrefixWithDefaultSeparator`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableFromObjectStringCast`." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTableIgnoresSeparatorWithoutPrefix`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetTable`." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetUnprefixedTableReturnsTableAsGiven`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsEmptyStringSeparatorWithoutPrefix`." +count = 1 + +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsInvalidPrefix`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "prefer-test-attribute" message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsInvalidSchema`." count = 1 +[[issues]] +file = "test/unit/Sql/TableIdentifierTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRejectsInvalidSeparator`." +count = 1 + [[issues]] file = "test/unit/Sql/TableIdentifierTest.php" code = "prefer-test-attribute" diff --git a/mago.toml b/mago.toml index 86ec9038..ae9cf5c7 100644 --- a/mago.toml +++ b/mago.toml @@ -1,5 +1,5 @@ extends = "vendor/php-db/phpdb-qa-tools/mago.toml" -php-version = "8.2.0" +php-version = "8.3.0" [source] paths = ["src", "test"] diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index aca6c95f..80febb9e 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -4,7 +4,7 @@ namespace PhpDb; -final class ConfigProvider +final readonly class ConfigProvider { public const NAMED_ADAPTER_KEY = 'adapters'; @@ -18,7 +18,8 @@ public function getDependencies(): array Adapter\AdapterInterface::class => Adapter\Adapter::class, ], 'factories' => [ - Adapter\Adapter::class => Container\AdapterInterfaceFactory::class, + Adapter\Adapter::class => Container\AdapterInterfaceFactory::class, + Sql\TableIdentifierFactory::class => Container\TableIdentifierFactoryFactory::class, ], ]; } diff --git a/src/Container/TableIdentifierFactoryFactory.php b/src/Container/TableIdentifierFactoryFactory.php new file mode 100644 index 00000000..d79fcd63 --- /dev/null +++ b/src/Container/TableIdentifierFactoryFactory.php @@ -0,0 +1,52 @@ + + * return [ + * TableIdentifierFactory::class => [ + * 'prefix' => 'backup', + * 'separator' => '_', + * ], + * ]; + * + * + * When no configuration is present the factory is created without a prefix. + * The separator defaults to '_' when not configured. + */ +final class TableIdentifierFactoryFactory +{ + /** + * @throws InvalidArgumentException If the configured prefix or separator is an empty string. + * @throws NotFoundExceptionInterface If the config service cannot be resolved. + * @throws ContainerExceptionInterface If retrieving the config service fails. + */ + public function __invoke(ContainerInterface $container): TableIdentifierFactory + { + /** @var array $config */ + $config = $container->has('config') ? $container->get('config') ?? [] : []; + + /** @var array{prefix?: string|null, separator?: string} $factoryConfig */ + $factoryConfig = $config[TableIdentifierFactory::class] ?? []; + + return new TableIdentifierFactory( + $factoryConfig['prefix'] ?? null, + $factoryConfig['separator'] ?? TableIdentifier::SEPARATOR, + ); + } +} diff --git a/src/Sql/TableIdentifier.php b/src/Sql/TableIdentifier.php index de63add7..5537b3b7 100644 --- a/src/Sql/TableIdentifier.php +++ b/src/Sql/TableIdentifier.php @@ -4,46 +4,83 @@ namespace PhpDb\Sql; -class TableIdentifier +final readonly class TableIdentifier { - protected string $table; + public const string SEPARATOR = '_'; - protected ?string $schema = null; - - public function __construct(string $table, ?string $schema = null) - { + /** + * @throws Exception\InvalidArgumentException If $table, $schema, $prefix or $separator is an empty string. + */ + public function __construct( + protected string $table, + protected ?string $schema = null, + protected ?string $prefix = null, + protected ?string $separator = self::SEPARATOR, + ) { if ('' === $table) { throw new Exception\InvalidArgumentException( '$table must be a valid table name, empty string given', ); } - $this->table = $table; + if ('' === $schema) { + throw new Exception\InvalidArgumentException( + '$schema must be a valid schema name or null, empty string given', + ); + } - if (null !== $schema) { - if ('' === $schema) { - throw new Exception\InvalidArgumentException( - '$schema must be a valid schema name or null, empty string given', - ); - } + if ('' === $prefix) { + throw new Exception\InvalidArgumentException( + '$prefix must be a valid table prefix or null, empty string given', + ); + } - $this->schema = $schema; + if ('' === $separator) { + throw new Exception\InvalidArgumentException( + '$separator must be a valid table separator, empty string given', + ); } } + public function getPrefix(): ?string + { + return $this->prefix; + } + public function getSchema(): ?string { return $this->schema; } + public function getSeparator(): string + { + return $this->separator ?? self::SEPARATOR; + } + + /** + * Returns the table name with the prefix and separator applied, when a + * prefix is set. + */ public function getTable(): string { - return $this->table; + if (null === $this->prefix) { + return $this->table; + } + + return $this->prefix . $this->getSeparator() . $this->table; } /** @return array{0: string, 1: null|string} */ public function getTableAndSchema(): array { - return [$this->table, $this->schema]; + return [$this->getTable(), $this->schema]; + } + + /** + * Returns the table name as given, without the prefix applied. + */ + public function getUnprefixedTable(): string + { + return $this->table; } } diff --git a/src/Sql/TableIdentifierFactory.php b/src/Sql/TableIdentifierFactory.php new file mode 100644 index 00000000..af0d23c1 --- /dev/null +++ b/src/Sql/TableIdentifierFactory.php @@ -0,0 +1,65 @@ +prefix; + } + + public function getSeparator(): string + { + return $this->separator ?? TableIdentifier::SEPARATOR; + } + + /** + * Creates a TableIdentifier carrying the configured prefix and separator. + * + * A prefix or separator passed at call time takes precedence over the + * configured one. + * + * @throws Exception\InvalidArgumentException If $prefix or $separator is an empty string. + */ + public function __invoke( + string $table, + ?string $schema = null, + ?string $prefix = null, + ?string $separator = null, + ): TableIdentifier { + return new TableIdentifier($table, $schema, $prefix ?? $this->prefix, $separator ?? $this->getSeparator()); + } +} diff --git a/test/unit/ConfigProviderTest.php b/test/unit/ConfigProviderTest.php index 9fc3a0e2..085f05ee 100644 --- a/test/unit/ConfigProviderTest.php +++ b/test/unit/ConfigProviderTest.php @@ -7,8 +7,13 @@ use PhpDb\Adapter; use PhpDb\ConfigProvider; use PhpDb\Container; +use PhpDb\Sql; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; +#[CoversClass(ConfigProvider::class)] +#[Group('unit')] class ConfigProviderTest extends TestCase { /** @@ -29,7 +34,8 @@ class ConfigProviderTest extends TestCase Adapter\AdapterInterface::class => Adapter\Adapter::class, ], 'factories' => [ - Adapter\Adapter::class => Container\AdapterInterfaceFactory::class, + Adapter\Adapter::class => Container\AdapterInterfaceFactory::class, + Sql\TableIdentifierFactory::class => Container\TableIdentifierFactoryFactory::class, ], ], ]; diff --git a/test/unit/Container/TableIdentifierFactoryFactoryTest.php b/test/unit/Container/TableIdentifierFactoryFactoryTest.php new file mode 100644 index 00000000..af57a627 --- /dev/null +++ b/test/unit/Container/TableIdentifierFactoryFactoryTest.php @@ -0,0 +1,143 @@ +setService('config', [ + TableIdentifierFactory::class => [ + 'prefix' => 'backup', + ], + ]); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertSame('backup', $result->getPrefix()); + } + + public function testInvokeCreatesFactoryWithConfiguredSeparator(): void + { + $container = new ServiceManager(); + $container->setService('config', [ + TableIdentifierFactory::class => [ + 'prefix' => 'backup', + 'separator' => '__', + ], + ]); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertSame('__', $result->getSeparator()); + } + + public function testInvokeCreatesFactoryWithConfiguredSeparatorWithoutPrefix(): void + { + $container = new ServiceManager(); + $container->setService('config', [ + TableIdentifierFactory::class => [ + 'separator' => '__', + ], + ]); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertNull($result->getPrefix()); + self::assertSame('__', $result->getSeparator()); + } + + public function testInvokeCreatesFactoryWithoutPrefixWhenConfigIsEmpty(): void + { + $container = new ServiceManager(); + $container->setService('config', []); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertNull($result->getPrefix()); + } + + public function testInvokeCreatesFactoryWithoutPrefixWhenConfigServiceIsNull(): void + { + $container = $this->createMock(ContainerInterface::class); + $container->method('has')->with('config')->willReturn(true); + $container->method('get')->with('config')->willReturn(null); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertNull($result->getPrefix()); + } + + public function testInvokeCreatesFactoryWithoutPrefixWhenContainerHasNoConfig(): void + { + $container = new ServiceManager(); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertNull($result->getPrefix()); + } + + public function testInvokeCreatesFactoryWithoutPrefixWhenPrefixKeyIsAbsent(): void + { + $container = new ServiceManager(); + $container->setService('config', [ + TableIdentifierFactory::class => [], + ]); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertNull($result->getPrefix()); + } + + public function testInvokeRejectsEmptyStringSeparatorFromConfig(): void + { + $container = new ServiceManager(); + $container->setService('config', [ + TableIdentifierFactory::class => [ + 'separator' => '', + ], + ]); + + $factory = new TableIdentifierFactoryFactory(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$separator must be a valid table separator, empty string given'); + $factory($container); + } + + public function testInvokeUsesDefaultSeparatorWhenSeparatorKeyIsAbsent(): void + { + $container = new ServiceManager(); + $container->setService('config', [ + TableIdentifierFactory::class => [ + 'prefix' => 'backup', + ], + ]); + + $factory = new TableIdentifierFactoryFactory(); + $result = $factory($container); + + self::assertSame('_', $result->getSeparator()); + } +} diff --git a/test/unit/Sql/TableIdentifierFactoryTest.php b/test/unit/Sql/TableIdentifierFactoryTest.php new file mode 100644 index 00000000..a3139aed --- /dev/null +++ b/test/unit/Sql/TableIdentifierFactoryTest.php @@ -0,0 +1,179 @@ +getPrefix()); + self::assertSame('archive_users', $tableIdentifier->getTable()); + } + + public function testCallTimePrefixOverridesConfiguredPrefix(): void + { + $factory = new TableIdentifierFactory('backup'); + $tableIdentifier = $factory('users', null, 'archive'); + + self::assertSame('archive', $tableIdentifier->getPrefix()); + self::assertSame('archive_users', $tableIdentifier->getTable()); + } + + public function testCallTimeSeparatorIsAppliedWhenNonePreconfigured(): void + { + $factory = new TableIdentifierFactory(); + $tableIdentifier = $factory('users', null, 'archive', '__'); + + self::assertSame('__', $tableIdentifier->getSeparator()); + self::assertSame('archive__users', $tableIdentifier->getTable()); + } + + public function testCallTimeSeparatorOverridesConfiguredSeparator(): void + { + $factory = new TableIdentifierFactory('backup', '__'); + $tableIdentifier = $factory('users', null, null, '_'); + + self::assertSame('_', $tableIdentifier->getSeparator()); + self::assertSame('backup_users', $tableIdentifier->getTable()); + } + + public function testConfiguredSeparatorIsCarriedButUnusedWhenNoPrefixApplies(): void + { + $factory = new TableIdentifierFactory(null, '__'); + $tableIdentifier = $factory('users'); + + self::assertSame('__', $tableIdentifier->getSeparator()); + self::assertNull($tableIdentifier->getPrefix()); + self::assertSame('users', $tableIdentifier->getTable()); + } + + public function testCreatesIdentifierWithConfiguredPrefix(): void + { + $factory = new TableIdentifierFactory('backup'); + $tableIdentifier = $factory('users'); + + self::assertSame('backup', $tableIdentifier->getPrefix()); + self::assertSame('backup_users', $tableIdentifier->getTable()); + self::assertNull($tableIdentifier->getSchema()); + } + + public function testCreatesIdentifierWithConfiguredSeparator(): void + { + $factory = new TableIdentifierFactory('backup', '__'); + $tableIdentifier = $factory('users'); + + self::assertSame('__', $tableIdentifier->getSeparator()); + self::assertSame('backup__users', $tableIdentifier->getTable()); + } + + public function testCreatesIdentifierWithoutPrefixWhenNoneConfigured(): void + { + $factory = new TableIdentifierFactory(); + $tableIdentifier = $factory('users', 'public'); + + self::assertNull($tableIdentifier->getPrefix()); + self::assertSame('users', $tableIdentifier->getTable()); + } + + public function testCreatesIdentifierWithSchema(): void + { + $factory = new TableIdentifierFactory('backup'); + $tableIdentifier = $factory('users', 'public'); + + self::assertSame(['backup_users', 'public'], $tableIdentifier->getTableAndSchema()); + } + + public function testPrefixIsNullByDefault(): void + { + $factory = new TableIdentifierFactory(); + + self::assertNull($factory->getPrefix()); + } + + public function testRejectsEmptyStringCallTimePrefix(): void + { + $factory = new TableIdentifierFactory('backup'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$prefix must be a valid table prefix or null, empty string given'); + $factory('users', null, ''); + } + + public function testRejectsEmptyStringCallTimeSeparator(): void + { + $factory = new TableIdentifierFactory('backup'); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$separator must be a valid table separator, empty string given'); + $factory('users', null, null, ''); + } + + public function testRejectsEmptyStringPrefix(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$prefix must be a valid table prefix or null, empty string given'); + new TableIdentifierFactory(''); + } + + public function testRejectsEmptyStringSeparator(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$separator must be a valid table separator, empty string given'); + new TableIdentifierFactory('backup', ''); + } + + public function testRejectsEmptyStringSeparatorWithoutPrefix(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$separator must be a valid table separator, empty string given'); + new TableIdentifierFactory(null, ''); + } + + public function testReturnsConfiguredPrefix(): void + { + $factory = new TableIdentifierFactory('backup'); + + self::assertSame('backup', $factory->getPrefix()); + } + + public function testReturnsConfiguredSeparator(): void + { + $factory = new TableIdentifierFactory('backup', '__'); + + self::assertSame('__', $factory->getSeparator()); + } + + public function testSeparatorDefaultsToUnderscore(): void + { + $factory = new TableIdentifierFactory('backup'); + + self::assertSame('_', $factory->getSeparator()); + } + + public function testSeparatorDefaultsToUnderscoreWhenNoPrefixConfigured(): void + { + $factory = new TableIdentifierFactory(); + + self::assertSame('_', $factory->getSeparator()); + } + + public function testSeparatorFallsBackToDefaultWhenPassedAsNull(): void + { + $factory = new TableIdentifierFactory('backup', null); + + self::assertSame('_', $factory->getSeparator()); + } +} diff --git a/test/unit/Sql/TableIdentifierTest.php b/test/unit/Sql/TableIdentifierTest.php index c0b85f0c..27b66a7f 100644 --- a/test/unit/Sql/TableIdentifierTest.php +++ b/test/unit/Sql/TableIdentifierTest.php @@ -9,6 +9,7 @@ use PhpDbTest\TestAsset\ObjectToString; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use stdClass; use TypeError; @@ -17,6 +18,7 @@ * Tests for {@see TableIdentifier} */ #[CoversClass(TableIdentifier::class)] +#[Group('unit')] class TableIdentifierTest extends TestCase { /** @@ -24,12 +26,12 @@ class TableIdentifierTest extends TestCase * * @return array[] */ - public static function invalidSchemaProvider(): array + public static function invalidNameArgumentProvider(): array { return [ - [''], - [new stdClass()], - [[]], + 'empty string' => [''], + 'object' => [new stdClass()], + 'array' => [[]], ]; } @@ -41,11 +43,18 @@ public static function invalidSchemaProvider(): array public static function invalidTableProvider(): array { return [ - [null], - ...self::invalidSchemaProvider(), + 'null' => [null], + ...self::invalidNameArgumentProvider(), ]; } + public function testGetDefaultPrefix(): void + { + $tableIdentifier = new TableIdentifier('foo'); + + self::assertNull($tableIdentifier->getPrefix()); + } + public function testGetDefaultSchema(): void { $tableIdentifier = new TableIdentifier('foo'); @@ -53,6 +62,20 @@ public function testGetDefaultSchema(): void self::assertNull($tableIdentifier->getSchema()); } + public function testGetDefaultSeparator(): void + { + $tableIdentifier = new TableIdentifier('foo'); + + self::assertSame('_', $tableIdentifier->getSeparator()); + } + + public function testGetPrefix(): void + { + $tableIdentifier = new TableIdentifier('foo', null, 'backup'); + + self::assertSame('backup', $tableIdentifier->getPrefix()); + } + public function testGetSchema(): void { $tableIdentifier = new TableIdentifier('foo', 'bar'); @@ -72,6 +95,13 @@ public function testGetSchemaFromObjectStringCast(): void self::assertSame('castResult', $tableIdentifier->getSchema()); } + public function testGetSeparator(): void + { + $tableIdentifier = new TableIdentifier('foo', null, 'backup', '__'); + + self::assertSame('__', $tableIdentifier->getSeparator()); + } + public function testGetTable(): void { $tableIdentifier = new TableIdentifier('foo'); @@ -79,6 +109,34 @@ public function testGetTable(): void self::assertSame('foo', $tableIdentifier->getTable()); } + public function testGetTableAndSchemaAppliesPrefix(): void + { + $tableIdentifier = new TableIdentifier('foo', 'bar', 'backup'); + + self::assertSame(['backup_foo', 'bar'], $tableIdentifier->getTableAndSchema()); + } + + public function testGetTableAndSchemaWithoutPrefix(): void + { + $tableIdentifier = new TableIdentifier('foo', 'bar'); + + self::assertSame(['foo', 'bar'], $tableIdentifier->getTableAndSchema()); + } + + public function testGetTableAppliesPrefixWithCustomSeparator(): void + { + $tableIdentifier = new TableIdentifier('foo', null, 'backup', '__'); + + self::assertSame('backup__foo', $tableIdentifier->getTable()); + } + + public function testGetTableAppliesPrefixWithDefaultSeparator(): void + { + $tableIdentifier = new TableIdentifier('foo', null, 'backup'); + + self::assertSame('backup_foo', $tableIdentifier->getTable()); + } + public function testGetTableFromObjectStringCast(): void { $table = new ObjectToString('castResult'); @@ -88,7 +146,36 @@ public function testGetTableFromObjectStringCast(): void self::assertSame('castResult', $tableIdentifier->getTable()); } - #[DataProvider('invalidSchemaProvider')] + public function testGetTableIgnoresSeparatorWithoutPrefix(): void + { + $tableIdentifier = new TableIdentifier('foo', null, null, '__'); + + self::assertSame('foo', $tableIdentifier->getTable()); + } + + public function testGetUnprefixedTableReturnsTableAsGiven(): void + { + $tableIdentifier = new TableIdentifier('foo', null, 'backup'); + + self::assertSame('foo', $tableIdentifier->getUnprefixedTable()); + } + + public function testRejectsEmptyStringSeparatorWithoutPrefix(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$separator must be a valid table separator, empty string given'); + new TableIdentifier('foo', null, null, ''); + } + + #[DataProvider('invalidNameArgumentProvider')] + public function testRejectsInvalidPrefix(mixed $invalidPrefix): void + { + $this->expectException('' === $invalidPrefix ? InvalidArgumentException::class : TypeError::class); + /** @psalm-suppress MixedArgument */ + new TableIdentifier('foo', 'bar', $invalidPrefix); + } + + #[DataProvider('invalidNameArgumentProvider')] public function testRejectsInvalidSchema(mixed $invalidSchema): void { $this->expectException('' === $invalidSchema ? InvalidArgumentException::class : TypeError::class); @@ -96,6 +183,14 @@ public function testRejectsInvalidSchema(mixed $invalidSchema): void new TableIdentifier('foo', $invalidSchema); } + #[DataProvider('invalidNameArgumentProvider')] + public function testRejectsInvalidSeparator(mixed $invalidSeparator): void + { + $this->expectException('' === $invalidSeparator ? InvalidArgumentException::class : TypeError::class); + /** @psalm-suppress MixedArgument */ + new TableIdentifier('foo', 'bar', 'backup', $invalidSeparator); + } + #[DataProvider('invalidTableProvider')] public function testRejectsInvalidTable(mixed $invalidTable): void {