From 0d570cce4017037b30e18eb43b015cd407ee1358 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 22 Oct 2024 19:24:10 +0200 Subject: [PATCH 1/4] refactor: fix proxy system and introduce psalm extension (#704) Co-authored-by: nikophil --- .github/workflows/ci.yml | 11 +- bin/tools/phpstan/composer.lock | 36 +- bin/tools/psalm/composer.lock | 462 +++++++++--------- composer.json | 8 +- docs/index.rst | 8 +- phpstan.neon | 2 +- psalm.xml | 11 +- src/Factory.php | 42 +- src/FactoryCollection.php | 2 +- src/ObjectFactory.php | 18 +- src/Persistence/PersistentObjectFactory.php | 82 +--- .../PersistentProxyObjectFactory.php | 73 ++- src/Persistence/Proxy.php | 34 ++ src/Persistence/ProxyRepositoryDecorator.php | 44 ++ src/Persistence/RepositoryDecorator.php | 10 +- src/phpunit_helper.php | 2 +- stubs/PersistentObjectFactory.php | 70 --- stubs/PersistentProxyObjectFactory.php | 66 --- stubs/functions.php | 26 - stubs/phpstan/ObjectFactory.php | 69 +++ stubs/phpstan/PersistentObjectFactory.php | 118 +++++ .../phpstan/PersistentProxyObjectFactory.php | 134 +++++ stubs/phpstan/functions.php | 26 + stubs/psalm/ObjectFactory.php | 63 +++ stubs/psalm/PersistentObjectFactory.php | 114 +++++ stubs/psalm/PersistentProxyObjectFactory.php | 122 +++++ stubs/psalm/functions.php | 39 ++ .../FixProxyFactoryMethodsReturnType.php | 63 +++ utils/psalm/FoundryPlugin.php | 16 + 29 files changed, 1219 insertions(+), 552 deletions(-) delete mode 100644 stubs/PersistentObjectFactory.php delete mode 100644 stubs/PersistentProxyObjectFactory.php delete mode 100644 stubs/functions.php create mode 100644 stubs/phpstan/ObjectFactory.php create mode 100644 stubs/phpstan/PersistentObjectFactory.php create mode 100644 stubs/phpstan/PersistentProxyObjectFactory.php create mode 100644 stubs/phpstan/functions.php create mode 100644 stubs/psalm/ObjectFactory.php create mode 100644 stubs/psalm/PersistentObjectFactory.php create mode 100644 stubs/psalm/PersistentProxyObjectFactory.php create mode 100644 stubs/psalm/functions.php create mode 100644 utils/psalm/FixProxyFactoryMethodsReturnType.php create mode 100644 utils/psalm/FoundryPlugin.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5df09b069..0d5bced4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,12 +242,11 @@ jobs: - name: Run static analysis run: bin/tools/phpstan/vendor/phpstan/phpstan/phpstan analyse - # there are some problem with psalm and bc layer, around proxy - # - name: Install Psalm - # run: composer bin psalm install - # - # - name: Run Psalm on factories generated with maker - # run: bin/tools/psalm/vendor/vimeo/psalm/psalm + - name: Install Psalm + run: composer bin psalm install + + - name: Run Psalm on factories generated with maker + run: bin/tools/psalm/vendor/vimeo/psalm/psalm fixcs: name: Run php-cs-fixer diff --git a/bin/tools/phpstan/composer.lock b/bin/tools/phpstan/composer.lock index a8b309c85..153ae99e8 100644 --- a/bin/tools/phpstan/composer.lock +++ b/bin/tools/phpstan/composer.lock @@ -117,16 +117,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.11.10", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/640410b32995914bde3eed26fa89552f9c2c082f", - "reference": "640410b32995914bde3eed26fa89552f9c2c082f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -171,20 +171,20 @@ "type": "github" } ], - "time": "2024-08-08T09:02:50+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpstan/phpstan-doctrine", - "version": "1.5.0", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "caa046bd6152818e781260fb3a7a96d6b0fadeed" + "reference": "38db3bad8f1567d7bf64806738d724261f8a2b5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/caa046bd6152818e781260fb3a7a96d6b0fadeed", - "reference": "caa046bd6152818e781260fb3a7a96d6b0fadeed", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/38db3bad8f1567d7bf64806738d724261f8a2b5c", + "reference": "38db3bad8f1567d7bf64806738d724261f8a2b5c", "shasum": "" }, "require": { @@ -241,9 +241,9 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.5.0" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/1.5.3" }, - "time": "2024-08-05T13:47:07+00:00" + "time": "2024-09-01T13:17:34+00:00" }, { "name": "phpstan/phpstan-phpunit", @@ -299,22 +299,22 @@ }, { "name": "phpstan/phpstan-symfony", - "version": "1.4.6", + "version": "1.4.10", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "e909a075d69e0d4db262ac3407350ae2c6b6ab5f" + "reference": "f7d5782044bedf93aeb3f38e09c91148ee90e5a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/e909a075d69e0d4db262ac3407350ae2c6b6ab5f", - "reference": "e909a075d69e0d4db262ac3407350ae2c6b6ab5f", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/f7d5782044bedf93aeb3f38e09c91148ee90e5a1", + "reference": "f7d5782044bedf93aeb3f38e09c91148ee90e5a1", "shasum": "" }, "require": { "ext-simplexml": "*", "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.11.7" + "phpstan/phpstan": "^1.12" }, "conflict": { "symfony/framework-bundle": "<3.0" @@ -365,9 +365,9 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.4.6" + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.4.10" }, - "time": "2024-07-16T11:48:54+00:00" + "time": "2024-09-26T18:14:50+00:00" } ], "packages-dev": [], diff --git a/bin/tools/psalm/composer.lock b/bin/tools/psalm/composer.lock index 13d03967a..a61d622fb 100644 --- a/bin/tools/psalm/composer.lock +++ b/bin/tools/psalm/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "amphp/amp", - "version": "v2.6.2", + "version": "v2.6.4", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", "shasum": "" }, "require": { @@ -29,8 +29,8 @@ "ext-json": "*", "jetbrains/phpstorm-stubs": "^2019.3", "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", "extra": { @@ -85,7 +85,7 @@ "support": { "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" + "source": "https://github.com/amphp/amp/tree/v2.6.4" }, "funding": [ { @@ -93,20 +93,20 @@ "type": "github" } ], - "time": "2022-02-20T17:52:18+00:00" + "time": "2024-03-21T18:52:26+00:00" }, { "name": "amphp/byte-stream", - "version": "v1.8.1", + "version": "v1.8.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, "require": { @@ -122,11 +122,6 @@ "psalm/phar": "^3.11.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "files": [ "lib/functions.php" @@ -150,7 +145,7 @@ } ], "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", + "homepage": "https://amphp.org/byte-stream", "keywords": [ "amp", "amphp", @@ -160,9 +155,8 @@ "stream" ], "support": { - "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" }, "funding": [ { @@ -170,34 +164,42 @@ "type": "github" } ], - "time": "2021-03-30T17:13:30+00:00" + "time": "2024-04-13T18:00:56+00:00" }, { "name": "composer/pcre", - "version": "3.1.1", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "url": "https://api.github.com/repos/composer/pcre/zipball/63aaeac21d7e775ff9bc9d45021e1745c97521c4", + "reference": "63aaeac21d7e775ff9bc9d45021e1745c97521c4", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, "require-dev": { - "phpstan/phpstan": "^1.3", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { "branch-alias": { "dev-main": "3.x-dev" + }, + "phpstan": { + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -225,7 +227,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.1" + "source": "https://github.com/composer/pcre/tree/3.3.1" }, "funding": [ { @@ -241,28 +243,28 @@ "type": "tidelift" } ], - "time": "2023-10-11T07:11:09+00:00" + "time": "2024-08-27T18:44:43+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -306,7 +308,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -322,20 +324,20 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "composer/xdebug-handler", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { @@ -346,7 +348,7 @@ "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -370,9 +372,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { @@ -388,7 +390,7 @@ "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { "name": "dnoegel/php-xdg-base-dir", @@ -429,16 +431,16 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { @@ -470,9 +472,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2023-09-27T20:04:15+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "felixfbecker/advanced-json-rpc", @@ -521,16 +523,16 @@ }, { "name": "felixfbecker/language-server-protocol", - "version": "v1.5.2", + "version": "v1.5.3", "source": { "type": "git", "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", "shasum": "" }, "require": { @@ -571,22 +573,22 @@ ], "support": { "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" }, - "time": "2022-03-02T22:36:06+00:00" + "time": "2024-04-30T00:40:11+00:00" }, { "name": "fidry/cpu-core-counter", - "version": "0.5.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623" + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/b58e5a3933e541dc286cc91fc4f3898bbc6f1623", - "reference": "b58e5a3933e541dc286cc91fc4f3898bbc6f1623", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { @@ -594,13 +596,13 @@ }, "require-dev": { "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", "phpstan/phpstan": "^1.9.2", "phpstan/phpstan-deprecation-rules": "^1.0.0", "phpstan/phpstan-phpunit": "^1.2.2", "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^9.5.26 || ^8.5.31", - "theofidry/php-cs-fixer-config": "^1.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, "type": "library", @@ -626,7 +628,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/0.5.1" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" }, "funding": [ { @@ -634,20 +636,20 @@ "type": "github" } ], - "time": "2022-12-24T12:35:10+00:00" + "time": "2024-08-06T10:04:20+00:00" }, { "name": "netresearch/jsonmapper", - "version": "v4.2.0", + "version": "v4.5.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", - "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", "shasum": "" }, "require": { @@ -658,7 +660,7 @@ "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", "squizlabs/php_codesniffer": "~3.5" }, "type": "library", @@ -683,31 +685,31 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" }, - "time": "2023-04-09T17:37:40+00:00" + "time": "2024-09-08T10:13:13+00:00" }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v4.19.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.1" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" @@ -739,9 +741,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2024-09-29T15:01:53+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -798,28 +800,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "version": "5.4.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.1", "ext-filter": "*", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "mockery/mockery": "~1.3.5", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.13" }, "type": "library", "extra": { @@ -843,33 +852,33 @@ }, { "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "email": "opensource@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2024-05-21T05:55:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.3", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", - "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", + "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpstan/phpdoc-parser": "^1.13" }, @@ -907,22 +916,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" }, - "time": "2023-08-12T11:01:26+00:00" + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.24.4", + "version": "1.33.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496" + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496", - "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", "shasum": "" }, "require": { @@ -954,9 +963,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" }, - "time": "2023-11-26T18:29:22+00:00" + "time": "2024-10-13T11:25:22+00:00" }, { "name": "psr/container", @@ -1013,16 +1022,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -1057,35 +1066,35 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "sebastian/diff", - "version": "5.0.3", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0", + "phpunit/phpunit": "^11.0", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1118,7 +1127,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { @@ -1126,20 +1135,20 @@ "type": "github" } ], - "time": "2023-05-01T07:48:21+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { "name": "spatie/array-to-xml", - "version": "3.2.2", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "96be97e664c87613121d073ea39af4c74e57a7f8" + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/96be97e664c87613121d073ea39af4c74e57a7f8", - "reference": "96be97e664c87613121d073ea39af4c74e57a7f8", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/f56b220fe2db1ade4c88098d83413ebdfc3bf876", + "reference": "f56b220fe2db1ade4c88098d83413ebdfc3bf876", "shasum": "" }, "require": { @@ -1152,6 +1161,11 @@ "spatie/pest-plugin-snapshots": "^1.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { "Spatie\\ArrayToXml\\": "src" @@ -1177,7 +1191,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.2.2" + "source": "https://github.com/spatie/array-to-xml/tree/3.3.0" }, "funding": [ { @@ -1189,51 +1203,50 @@ "type": "github" } ], - "time": "2023-11-14T14:08:51+00:00" + "time": "2024-05-01T10:20:27+00:00" }, { "name": "symfony/console", - "version": "v6.4.1", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd" + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a550a7c99daeedef3f9d23fb82e3531525ff11fd", - "reference": "a550a7c99daeedef3f9d23fb82e3531525ff11fd", + "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1267,7 +1280,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.1" + "source": "https://github.com/symfony/console/tree/v7.1.5" }, "funding": [ { @@ -1283,20 +1296,20 @@ "type": "tidelift" } ], - "time": "2023-11-30T10:54:28+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -1305,7 +1318,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1334,7 +1347,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.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1350,27 +1363,30 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.0", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59" + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/952a8cb588c3bc6ce76f6023000fb932f16a6e59", - "reference": "952a8cb588c3bc6ce76f6023000fb932f16a6e59", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -1397,7 +1413,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.0" + "source": "https://github.com/symfony/filesystem/tree/v7.1.5" }, "funding": [ { @@ -1413,24 +1429,24 @@ "type": "tidelift" } ], - "time": "2023-07-26T17:27:13+00:00" + "time": "2024-09-17T09:16:35+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -1440,9 +1456,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1479,7 +1492,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1495,33 +1508,30 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1560,7 +1570,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -1576,33 +1586,30 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1644,7 +1651,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -1660,24 +1667,24 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1687,9 +1694,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1727,7 +1731,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1743,25 +1747,26 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -1769,7 +1774,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1809,7 +1814,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" }, "funding": [ { @@ -1825,24 +1830,24 @@ "type": "tidelift" } ], - "time": "2023-07-30T20:28:31+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/string", - "version": "v6.4.0", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809" + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/b45fcf399ea9c3af543a92edf7172ba21174d809", - "reference": "b45fcf399ea9c3af543a92edf7172ba21174d809", + "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -1852,11 +1857,12 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1895,7 +1901,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.0" + "source": "https://github.com/symfony/string/tree/v7.1.5" }, "funding": [ { @@ -1911,20 +1917,20 @@ "type": "tidelift" } ], - "time": "2023-11-28T20:41:49+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "vimeo/psalm", - "version": "5.17.0", + "version": "5.26.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "c620f6e80d0abfca532b00bda366062aaedf6e5d" + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/c620f6e80d0abfca532b00bda366062aaedf6e5d", - "reference": "c620f6e80d0abfca532b00bda366062aaedf6e5d", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", "shasum": "" }, "require": { @@ -1943,11 +1949,11 @@ "ext-tokenizer": "*", "felixfbecker/advanced-json-rpc": "^3.1", "felixfbecker/language-server-protocol": "^1.5.2", - "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1", + "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.16", + "nikic/php-parser": "^4.17", "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", - "sebastian/diff": "^4.0 || ^5.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" @@ -2021,7 +2027,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2023-12-03T20:21:41+00:00" + "time": "2024-09-08T18:53:08+00:00" }, { "name": "webmozart/assert", diff --git a/composer.json b/composer.json index 0e619e8b1..aec2e99e4 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,10 @@ "symfony/yaml": "^6.4|^7.0" }, "autoload": { - "psr-4": { "Zenstruck\\Foundry\\": "src/" }, + "psr-4": { + "Zenstruck\\Foundry\\": "src/", + "Zenstruck\\Foundry\\Psalm\\": "utils/psalm" + }, "files": ["src/functions.php", "src/Persistence/functions.php", "src/phpunit_helper.php"] }, "autoload-dev": { @@ -67,6 +70,9 @@ "target-directory": "bin/tools", "bin-links": true, "forward-command": false + }, + "psalm": { + "pluginClass": "Zenstruck\\Foundry\\Psalm\\FoundryPlugin" } }, "scripts": { diff --git a/docs/index.rst b/docs/index.rst index 579078db5..3e5a0da0a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -852,7 +852,7 @@ the LazyValue can be `memoized `_ so return [ // Call CategoryFactory::random() everytime this factory is instantiated 'category' => LazyValue::new(fn() => CategoryFactory::random()), - // The same User instance will be both added to the Project and set as the Task owner + // The same UserForPersistentFactory instance will be both added to the Project and set as the Task owner 'project' => ProjectFactory::new(['users' => [$owner]]), 'owner' => $owner, ]; @@ -888,7 +888,7 @@ common use-case: encoding a password with the ``UserPasswordHasherInterface`` se public static function class(): string { - return User::class; + return UserForPersistentFactory::class; } protected function defaults(): array @@ -902,7 +902,7 @@ common use-case: encoding a password with the ``UserPasswordHasherInterface`` se protected function initialize(): static { return $this - ->afterInstantiate(function(User $user) { + ->afterInstantiate(function(UserForPersistentFactory $user) { $user->setPassword($this->passwordHasher->hashPassword($user, $user->getPassword())); }) ; @@ -1748,7 +1748,7 @@ You can improve the speed by reducing the *work factor* of your encoder: # config/packages/test/security.yaml encoders: # use your user class name here - App\Entity\User: + App\Entity\UserForPersistentFactory: # This should be the same value as in config/packages/security.yaml algorithm: auto cost: 4 # Lowest possible value for bcrypt diff --git a/phpstan.neon b/phpstan.neon index 030b3b5e3..f3e6e4d76 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,7 +4,7 @@ parameters: paths: - src - tests - - stubs + - stubs/phpstan ignoreErrors: # suppress strange behavior of PHPStan where it considers proxy() return type as *NEVER* - message: '#Return type of call to function Zenstruck\\Foundry\\Persistence\\proxy contains unresolvable type#' diff --git a/psalm.xml b/psalm.xml index f6647a3ff..55f2d4718 100644 --- a/psalm.xml +++ b/psalm.xml @@ -9,12 +9,11 @@ findUnusedCode="false" > - - + + - - - - + + + diff --git a/src/Factory.php b/src/Factory.php index fdf337486..93869470b 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -25,7 +25,7 @@ */ abstract class Factory { - /** @var Attributes[] */ + /** @phpstan-var Attributes[] */ private array $attributes; // keep an empty constructor for BC @@ -33,9 +33,10 @@ public function __construct() { } - /** - * @param Attributes $attributes + * @return static + * @phpstan-return static + * @phpstan-param Attributes $attributes */ final public static function new(array|callable $attributes = []): static { @@ -53,19 +54,19 @@ final public static function new(array|callable $attributes = []): static } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * * @return T */ - final public static function createOne(array|callable $attributes = []): mixed + public static function createOne(array|callable $attributes = []): mixed { return static::new()->create($attributes); } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * - * @return T[] + * @return list */ final public static function createMany(int $number, array|callable $attributes = []): array { @@ -73,9 +74,9 @@ final public static function createMany(int $number, array|callable $attributes } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * - * @return T[] + * @return list */ final public static function createRange(int $min, int $max, array|callable $attributes = []): array { @@ -83,9 +84,9 @@ final public static function createRange(int $min, int $max, array|callable $att } /** - * @param Sequence $sequence + * @phpstan-param Sequence $sequence * - * @return T[] + * @return list */ final public static function createSequence(iterable|callable $sequence): array { @@ -93,7 +94,7 @@ final public static function createSequence(iterable|callable $sequence): array } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * * @return T */ @@ -116,7 +117,7 @@ final public function range(int $min, int $max): FactoryCollection } /** - * @param Sequence $sequence + * @phpstan-param Sequence $sequence * @return FactoryCollection */ final public function sequence(iterable|callable $sequence): FactoryCollection @@ -129,7 +130,10 @@ final public function sequence(iterable|callable $sequence): FactoryCollection } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes + * + * @psalm-return static + * @phpstan-return static */ final public function with(array|callable $attributes = []): static { @@ -147,9 +151,9 @@ final protected static function faker(): Faker\Generator /** * @internal * - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * - * @return Parameters + * @phpstan-return Parameters */ final protected function normalizeAttributes(array|callable $attributes = []): array { @@ -181,9 +185,9 @@ protected function initialize(): static /** * @internal * - * @param Parameters $parameters + * @phpstan-param Parameters $parameters * - * @return Parameters + * @phpstan-return Parameters */ protected function normalizeParameters(array $parameters): array { @@ -241,7 +245,7 @@ protected function normalizeObject(object $object): object } /** - * @return Attributes + * @phpstan-return Attributes */ abstract protected function defaults(): array|callable; } diff --git a/src/FactoryCollection.php b/src/FactoryCollection.php index d2a5304ab..0b11b252f 100644 --- a/src/FactoryCollection.php +++ b/src/FactoryCollection.php @@ -66,7 +66,7 @@ public static function sequence(Factory $factory, iterable $items): self /** * @param Attributes $attributes * - * @return T[] + * @return list */ public function create(array|callable $attributes = []): array { diff --git a/src/ObjectFactory.php b/src/ObjectFactory.php index 16b128874..f791ec89f 100644 --- a/src/ObjectFactory.php +++ b/src/ObjectFactory.php @@ -25,13 +25,13 @@ */ abstract class ObjectFactory extends Factory { - /** @var list):Parameters> */ + /** @phpstan-var list):Parameters> */ private array $beforeInstantiate = []; - /** @var list */ + /** @phpstan-var list */ private array $afterInstantiate = []; - /** @var InstantiatorCallable|null */ + /** @phpstan-var InstantiatorCallable|null */ private $instantiator; /** @@ -40,8 +40,6 @@ abstract class ObjectFactory extends Factory abstract public static function class(): string; /** - * @final - * * @return T */ public function create(callable|array $attributes = []): object @@ -58,6 +56,7 @@ public function create(callable|array $attributes = []): object $parameters = $this->normalizeParameters($parameters); $instantiator = $this->instantiator ?? Configuration::instance()->instantiator; + /** @var T $object */ $object = $instantiator($parameters, static::class()); foreach ($this->afterInstantiate as $hook) { @@ -68,7 +67,10 @@ public function create(callable|array $attributes = []): object } /** - * @param InstantiatorCallable $instantiator + * @phpstan-param InstantiatorCallable $instantiator + * + * @psalm-return static + * @phpstan-return static */ final public function instantiateWith(callable $instantiator): static { @@ -79,7 +81,7 @@ final public function instantiateWith(callable $instantiator): static } /** - * @param callable(Parameters,class-string):Parameters $callback + * @phpstan-param callable(Parameters,class-string):Parameters $callback */ final public function beforeInstantiate(callable $callback): static { @@ -92,7 +94,7 @@ final public function beforeInstantiate(callable $callback): static /** * @final * - * @param callable(T,Parameters):void $callback + * @phpstan-param callable(T,Parameters):void $callback */ public function afterInstantiate(callable $callback): static { diff --git a/src/Persistence/PersistentObjectFactory.php b/src/Persistence/PersistentObjectFactory.php index 63f723d5e..cdc484df5 100644 --- a/src/Persistence/PersistentObjectFactory.php +++ b/src/Persistence/PersistentObjectFactory.php @@ -34,16 +34,14 @@ abstract class PersistentObjectFactory extends ObjectFactory { private bool $persist; - /** @var list */ + /** @phpstan-var list */ private array $afterPersist = []; /** @var list */ private array $tempAfterPersist = []; /** - * @final - * - * @param mixed|Parameters $criteriaOrId + * @phpstan-param mixed|Parameters $criteriaOrId * * @return T * @@ -55,9 +53,7 @@ public static function find(mixed $criteriaOrId): object } /** - * @final - * - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T */ @@ -73,9 +69,7 @@ public static function findOrCreate(array $criteria): object } /** - * @final - * - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T */ @@ -89,12 +83,10 @@ public static function randomOrCreate(array $criteria = []): object } /** - * @final - * * @param positive-int $count - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * - * @return T[] + * @return list */ public static function randomSet(int $count, array $criteria = []): array { @@ -102,13 +94,11 @@ public static function randomSet(int $count, array $criteria = []): array } /** - * @final - * * @param int<0, max> $min * @param int<0, max> $max - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * - * @return T[] + * @return list */ public static function randomRange(int $min, int $max, array $criteria = []): array { @@ -116,11 +106,9 @@ public static function randomRange(int $min, int $max, array $criteria = []): ar } /** - * @final - * - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * - * @return T[] + * @return list */ public static function findBy(array $criteria): array { @@ -128,9 +116,7 @@ public static function findBy(array $criteria): array } /** - * @final - * - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T */ @@ -140,33 +126,33 @@ public static function random(array $criteria = []): object } /** - * @final - * * @return T * * @throws \RuntimeException If no objects exist */ public static function first(string $sortBy = 'id'): object { - return static::repository()->firstOrFail($sortBy); + /** @var T $object */ + $object = static::repository()->firstOrFail($sortBy); + + return $object; } /** - * @final - * * @return T * * @throws \RuntimeException If no objects exist */ public static function last(string $sortBy = 'id'): object { - return static::repository()->lastOrFail($sortBy); + /** @var T $object */ + $object = static::repository()->lastOrFail($sortBy); + + return $object; } /** - * @final - * - * @return T[] + * @return list */ public static function all(): array { @@ -174,8 +160,6 @@ public static function all(): array } /** - * @final - * * @return RepositoryDecorator> */ public static function repository(): ObjectRepository @@ -191,7 +175,7 @@ final public static function assert(): RepositoryAssertions } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ final public static function count(array $criteria = []): int { @@ -206,12 +190,12 @@ final public static function truncate(): void /** * @return T */ - final public function create(callable|array $attributes = []): object + public function create(callable|array $attributes = []): object { $object = parent::create($attributes); if (!$this->isPersisting()) { - return $this->proxy($object); + return $object; } $configuration = Configuration::instance(); @@ -238,7 +222,7 @@ final public function create(callable|array $attributes = []): object $configuration->persistence()->save($object); } - return $this->proxy($object); + return $object; } final public function andPersist(): static @@ -258,7 +242,7 @@ final public function withoutPersisting(): static } /** - * @param callable(T, Parameters):void $callback + * @phpstan-param callable(T, Parameters):void $callback */ final public function afterPersist(callable $callback): static { @@ -345,20 +329,4 @@ final protected function isPersisting(): bool return $this->persist ?? $config->isPersistenceAvailable() && $config->persistence()->isEnabled() && $config->persistence()->autoPersist(static::class()); } - - /** - * @param T $object - * - * @return T - */ - private function proxy(object $object): object - { - if (!$this instanceof PersistentProxyObjectFactory) { - return $object; - } - - $object = proxy($object); - - return $this->isPersisting() ? $object : $object->_disableAutoRefresh(); - } } diff --git a/src/Persistence/PersistentProxyObjectFactory.php b/src/Persistence/PersistentProxyObjectFactory.php index a8b4870cb..a2e78d0a0 100644 --- a/src/Persistence/PersistentProxyObjectFactory.php +++ b/src/Persistence/PersistentProxyObjectFactory.php @@ -13,33 +13,12 @@ use Doctrine\Persistence\ObjectRepository; use Zenstruck\Foundry\Configuration; -use Zenstruck\Foundry\Factory; -use Zenstruck\Foundry\Object\Instantiator; -use Zenstruck\Foundry\FactoryCollection; // keep me! /** * @author Kevin Bond * * @template T of object * @extends PersistentObjectFactory> - * - * @phpstan-type InstantiatorCallable = Instantiator|callable(Parameters,class-string):T - * @phpstan-import-type Parameters from Factory - * @phpstan-import-type Attributes from Factory - * - * @phpstan-method $this instantiateWith(InstantiatorCallable $instantiator) - * - * @phpstan-method FactoryCollection> sequence(iterable>|callable(): iterable> $sequence) - * @phpstan-method FactoryCollection> many(int $min, int|null $max = null) - * - * @phpstan-method static list> createSequence(iterable>|callable(): iterable> $sequence) - * @phpstan-method static list> createMany(int $number, array|callable $attributes = []) - * - * @method static Proxy createOne(Attributes $attributes = []) - * @phpstan-method static T&Proxy createOne(Attributes $attributes = []) - * - * @method Proxy create(Attributes $attributes = []) - * @phpstan-method T&Proxy create(Attributes $attributes = []) */ abstract class PersistentProxyObjectFactory extends PersistentObjectFactory { @@ -49,30 +28,48 @@ abstract class PersistentProxyObjectFactory extends PersistentObjectFactory abstract public static function class(): string; /** - * @return Proxy + * @return T|Proxy + * @phpstan-return T&Proxy + */ + public function create(callable|array $attributes = []): object + { + return proxy(parent::create($attributes)); // @phpstan-ignore function.unresolvableReturnType + } + + /** + * @return T|Proxy + * @phpstan-return T&Proxy + */ + public static function createOne(array|callable $attributes = []): mixed + { + return proxy(parent::createOne($attributes)); // @phpstan-ignore function.unresolvableReturnType + } + + /** + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function find(mixed $criteriaOrId): object { - return proxy(parent::find($criteriaOrId)); // @phpstan-ignore-line + return proxy(parent::find($criteriaOrId)); // @phpstan-ignore function.unresolvableReturnType } /** - * @return Proxy + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function findOrCreate(array $criteria): object { - return proxy(parent::findOrCreate($criteria)); // @phpstan-ignore-line + return proxy(parent::findOrCreate($criteria)); // @phpstan-ignore function.unresolvableReturnType } /** - * @return Proxy + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function randomOrCreate(array $criteria = []): object { - return proxy(parent::randomOrCreate($criteria)); // @phpstan-ignore-line + return proxy(parent::randomOrCreate($criteria)); // @phpstan-ignore function.unresolvableReturnType } /** @@ -80,7 +77,7 @@ final public static function randomOrCreate(array $criteria = []): object */ final public static function randomSet(int $count, array $criteria = []): array { - return \array_map(proxy(...), parent::randomSet($count, $criteria)); // @phpstan-ignore-line + return \array_map(proxy(...), parent::randomSet($count, $criteria)); } /** @@ -88,7 +85,7 @@ final public static function randomSet(int $count, array $criteria = []): array */ final public static function randomRange(int $min, int $max, array $criteria = []): array { - return \array_map(proxy(...), parent::randomRange($min, $max, $criteria)); // @phpstan-ignore-line + return \array_map(proxy(...), parent::randomRange($min, $max, $criteria)); } /** @@ -96,34 +93,34 @@ final public static function randomRange(int $min, int $max, array $criteria = [ */ final public static function findBy(array $criteria): array { - return \array_map(proxy(...), parent::findBy($criteria)); // @phpstan-ignore-line + return \array_map(proxy(...), parent::findBy($criteria)); } /** - * @return Proxy + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function random(array $criteria = []): object { - return proxy(parent::random($criteria)); // @phpstan-ignore-line + return proxy(parent::random($criteria)); // @phpstan-ignore function.unresolvableReturnType } /** - * @return Proxy + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function first(string $sortBy = 'id'): object { - return proxy(parent::first($sortBy)); // @phpstan-ignore-line + return proxy(parent::first($sortBy)); // @phpstan-ignore function.unresolvableReturnType } /** - * @return Proxy + * @return T|Proxy * @phpstan-return T&Proxy */ final public static function last(string $sortBy = 'id'): object { - return proxy(parent::last($sortBy)); // @phpstan-ignore-line + return proxy(parent::last($sortBy)); // @phpstan-ignore function.unresolvableReturnType } /** @@ -131,7 +128,7 @@ final public static function last(string $sortBy = 'id'): object */ final public static function all(): array { - return \array_map(proxy(...), parent::all()); // @phpstan-ignore-line + return \array_map(proxy(...), parent::all()); } /** @@ -141,6 +138,6 @@ final public static function repository(): ObjectRepository { Configuration::instance()->assertPersistanceEnabled(); - return new ProxyRepositoryDecorator(static::class()); // @phpstan-ignore-line + return new ProxyRepositoryDecorator(static::class()); // @phpstan-ignore argument.type, return.type } } diff --git a/src/Persistence/Proxy.php b/src/Persistence/Proxy.php index cbddf6af7..eaa49192d 100644 --- a/src/Persistence/Proxy.php +++ b/src/Persistence/Proxy.php @@ -21,23 +21,49 @@ */ interface Proxy { + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _enableAutoRefresh(): static; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _disableAutoRefresh(): static; /** * @param callable(static):void $callback + * @psalm-return T&Proxy + * @phpstan-return static */ public function _withoutAutoRefresh(callable $callback): static; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _save(): static; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _refresh(): static; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _delete(): static; public function _get(string $property): mixed; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _set(string $property, mixed $value): static; /** @@ -45,8 +71,16 @@ public function _set(string $property, mixed $value): static; */ public function _real(): object; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _assertPersisted(string $message = '{entity} is not persisted.'): static; + /** + * @psalm-return T&Proxy + * @phpstan-return static + */ public function _assertNotPersisted(string $message = '{entity} is persisted but it should not be.'): static; /** diff --git a/src/Persistence/ProxyRepositoryDecorator.php b/src/Persistence/ProxyRepositoryDecorator.php index 2005ec607..5e2d26787 100644 --- a/src/Persistence/ProxyRepositoryDecorator.php +++ b/src/Persistence/ProxyRepositoryDecorator.php @@ -22,56 +22,97 @@ */ final class ProxyRepositoryDecorator extends RepositoryDecorator // @phpstan-ignore-line { + /** + * @return T|Proxy|null + * @psalm-return (T&Proxy)|null + */ public function first(string $sortBy = 'id'): ?object { return $this->proxyNullableObject(parent::first($sortBy)); } + /** + * @return T|Proxy + * @psalm-return T&Proxy + */ public function firstOrFail(string $sortBy = 'id'): object { return proxy(parent::firstOrFail($sortBy)); } + /** + * @return T|Proxy|null + * @psalm-return (T&Proxy)|null + */ public function last(string $sortedField = 'id'): ?object { return $this->proxyNullableObject(parent::last($sortedField)); } + /** + * @return T|Proxy + * @psalm-return T&Proxy + */ public function lastOrFail(string $sortBy = 'id'): object { return proxy(parent::lastOrFail($sortBy)); } + /** + * @return T|Proxy|null + * @psalm-return (T&Proxy)|null + */ public function find($id): ?object { return $this->proxyNullableObject(parent::find($id)); } + /** + * @return T|Proxy + * @psalm-return T&Proxy + */ public function findOrFail(mixed $id): object { return proxy(parent::findOrFail($id)); } + /** + * @psalm-return array> + */ public function findAll(): array { return $this->proxyArray(parent::findAll()); } + /** + * @psalm-return array> + */ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->proxyArray(parent::findBy($criteria, $orderBy, $limit, $offset)); } + /** + * @return T|Proxy|null + * @psalm-return (T&Proxy)|null + */ public function findOneBy(array $criteria): ?object { return $this->proxyNullableObject(parent::findOneBy($criteria)); } + /** + * @return T|Proxy|null + * @psalm-return T&Proxy + */ public function random(array $criteria = []): object { return proxy(parent::random($criteria)); } + /** + * @psalm-return array> + */ public function randomSet(int $count, array $criteria = []): array { return $this->proxyArray( @@ -79,6 +120,9 @@ public function randomSet(int $count, array $criteria = []): array ); } + /** + * @psalm-return array> + */ public function randomRange(int $min, int $max, array $criteria = []): array { return $this->proxyArray( diff --git a/src/Persistence/RepositoryDecorator.php b/src/Persistence/RepositoryDecorator.php index dc622c690..805d99002 100644 --- a/src/Persistence/RepositoryDecorator.php +++ b/src/Persistence/RepositoryDecorator.php @@ -92,10 +92,16 @@ public function lastOrFail(string $sortBy = 'id'): object public function find($id): ?object { if (\is_array($id) && (empty($id) || !array_is_list($id))) { - return $this->findOneBy($id); + /** @var T|null $object */ + $object = $this->findOneBy($id); + + return $object; } - return $this->inner()->find(unproxy($id)); + /** @var T|null $object */ + $object = $this->inner()->find(unproxy($id)); + + return $object; } /** diff --git a/src/phpunit_helper.php b/src/phpunit_helper.php index e171ddece..1e5a7a2fe 100644 --- a/src/phpunit_helper.php +++ b/src/phpunit_helper.php @@ -31,7 +31,7 @@ function restorePhpUnitErrorHandler(): void } while (true) { - $previousHandler = \set_error_handler(static fn() => null); // @phpstan-ignore-line + $previousHandler = \set_error_handler(static fn() => null); // @phpstan-ignore argument.type \restore_error_handler(); $isPhpUnitErrorHandler = $previousHandler instanceof \PHPUnit\Runner\ErrorHandler; if (null === $previousHandler || $isPhpUnitErrorHandler) { diff --git a/stubs/PersistentObjectFactory.php b/stubs/PersistentObjectFactory.php deleted file mode 100644 index 5d55f7a61..000000000 --- a/stubs/PersistentObjectFactory.php +++ /dev/null @@ -1,70 +0,0 @@ - - */ -class UserRepository extends EntityRepository -{ - public function findByName(string $name): User - { - return new User(); - } -} - -/** - * The following method stubs are required for auto-completion in PhpStorm - * AND phpstan support. - * - * @extends PersistentObjectFactory - * - * @method User create(array|callable $attributes = []) - * @method static RepositoryDecorator|UserRepository repository() - * - * @phpstan-method static RepositoryDecorator repository() - */ -final class UserFactory extends PersistentObjectFactory -{ - public static function class(): string - { - return User::class; - } - - protected function defaults(): array|callable - { - return []; - } -} - -// test autocomplete with phpstorm -assertType('string', UserFactory::new()->create()->name); -assertType('string', UserFactory::createOne()->name); -assertType('string', UserFactory::new()->many(2)->create()[0]->name); -assertType('string', UserFactory::createMany(1)[0]->name); -assertType('string', UserFactory::first()->name); -assertType('string', UserFactory::last()->name); -assertType('string', UserFactory::find(1)->name); -assertType('string', UserFactory::all()[0]->name); -assertType('string', UserFactory::random()->name); -assertType('string', UserFactory::randomRange(1, 2)[0]->name); -assertType('string', UserFactory::randomSet(2)[0]->name); -assertType('string', UserFactory::findBy(['name' => 'foo'])[0]->name); -assertType('string', UserFactory::findOrCreate([])->name); -assertType('string', UserFactory::randomOrCreate([])->name); -assertType('string|null', UserFactory::repository()->find(1)?->name); -assertType('string', UserFactory::repository()->findAll()[0]->name); -assertType('string', UserFactory::repository()->findByName('foo')->name); -assertType('int', UserFactory::repository()->count()); -assertType('string', proxy(UserFactory::createOne())->name); -assertType('string', proxy(UserFactory::new()->create())->name); diff --git a/stubs/PersistentProxyObjectFactory.php b/stubs/PersistentProxyObjectFactory.php deleted file mode 100644 index c6a254f7b..000000000 --- a/stubs/PersistentProxyObjectFactory.php +++ /dev/null @@ -1,66 +0,0 @@ - - */ -class UserRepository1 extends EntityRepository -{ - public function findByName(string $name): User1 - { - return new User1(); - } -} - -/** - * The following method stubs are required for auto-completion in PhpStorm - * AND phpstan support. - * - * @extends PersistentProxyObjectFactory - * - * @method static RepositoryDecorator|UserRepository1 repository() - * - * @phpstan-method static RepositoryDecorator repository() - */ -final class User1Factory extends PersistentProxyObjectFactory -{ - public static function class(): string - { - return User1::class; - } - - protected function defaults(): array|callable - { - return []; - } -} - -// test autocomplete with phpstorm -assertType('string', User1Factory::new()->create()->_refresh()->name); -assertType('string', User1Factory::createOne()->_refresh()->name); -assertType('string', User1Factory::new()->many(2)->create()[0]->_refresh()->name); -assertType('string', User1Factory::createMany(1)[0]->_refresh()->name); -assertType('string', User1Factory::first()->_refresh()->name); -assertType('string', User1Factory::last()->_refresh()->name); -assertType('string', User1Factory::find(1)->_refresh()->name); -assertType('string', User1Factory::all()[0]->_refresh()->name); -assertType('string', User1Factory::random()->_refresh()->name); -assertType('string', User1Factory::randomRange(1, 2)[0]->_refresh()->name); -assertType('string', User1Factory::randomSet(2)[0]->_refresh()->name); -assertType('string', User1Factory::findBy(['name' => 'foo'])[0]->_refresh()->name); -assertType('string', User1Factory::findOrCreate([])->_refresh()->name); -assertType('string', User1Factory::randomOrCreate([])->_refresh()->name); -assertType('string|null', User1Factory::repository()->find(1)?->name); -assertType('string', User1Factory::repository()->findAll()[0]->name); -assertType('string', User1Factory::repository()->findByName('foo')->name); -assertType('int', User1Factory::repository()->count()); diff --git a/stubs/functions.php b/stubs/functions.php deleted file mode 100644 index e5fc30c3f..000000000 --- a/stubs/functions.php +++ /dev/null @@ -1,26 +0,0 @@ -create()->name); -assertType('string', object(User::class)->name); - -assertType('string', persistent_factory(User::class)->create()->name); -assertType('string', persist(User::class)->name); - -assertType('User|null', repository(User::class)->find(1)); - -assertType('User&Zenstruck\Foundry\Persistence\Proxy', proxy(object(User::class))); -assertType('string', proxy(object(User::class))->name); -assertType('string', proxy(object(User::class))->_refresh()->name); diff --git a/stubs/phpstan/ObjectFactory.php b/stubs/phpstan/ObjectFactory.php new file mode 100644 index 000000000..a90cb8e19 --- /dev/null +++ b/stubs/phpstan/ObjectFactory.php @@ -0,0 +1,69 @@ + + */ +final class UserObjectFactory extends ObjectFactory +{ + public static function class(): string + { + return UserForObjectFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +// methods returning one object +assertType('UserForObjectFactory', UserObjectFactory::new()->create()); +assertType('UserForObjectFactory', UserObjectFactory::createOne()); +assertType( + 'UserForObjectFactory', + UserObjectFactory::new()->instantiateWith(Instantiator::withConstructor())->create() +); +assertType('UserForObjectFactory', UserObjectFactory::new()->with()->create()); + +// methods returning a list of objects +assertType("array", UserObjectFactory::createMany(1)); +assertType("array", UserObjectFactory::createRange(1, 2)); +assertType("array", UserObjectFactory::createSequence([])); + +// methods with FactoryCollection +$factoryCollection = FactoryCollection::class; +assertType("{$factoryCollection}", UserObjectFactory::new()->many(2)); +assertType("{$factoryCollection}", UserObjectFactory::new()->range(1, 2)); +assertType("{$factoryCollection}", UserObjectFactory::new()->sequence([])); +assertType("array", UserObjectFactory::new()->many(2)->create()); +assertType("array", UserObjectFactory::new()->range(1, 2)->create()); +assertType("array", UserObjectFactory::new()->sequence([])->create()); + +// test autocomplete with phpstorm +assertType('string', UserObjectFactory::new()->create()->name); +assertType('string', UserObjectFactory::new()->instantiateWith(Instantiator::withConstructor())->create()->name); +assertType('string', UserObjectFactory::new()->with()->create()->name); +assertType('string', UserObjectFactory::createOne()->name); + +assertType("string", UserObjectFactory::createMany(1)[0]->name); +assertType("string", UserObjectFactory::createRange(1, 2)[0]->name); +assertType("string", UserObjectFactory::createSequence([])[0]->name); + +assertType("string", UserObjectFactory::new()->many(2)->create()[0]->name); +assertType("string", UserObjectFactory::new()->range(1, 2)->create()[0]->name); +assertType("string", UserObjectFactory::new()->sequence([])->create()[0]->name); diff --git a/stubs/phpstan/PersistentObjectFactory.php b/stubs/phpstan/PersistentObjectFactory.php new file mode 100644 index 000000000..ff946514b --- /dev/null +++ b/stubs/phpstan/PersistentObjectFactory.php @@ -0,0 +1,118 @@ + + */ +final class UserFactory extends PersistentObjectFactory +{ + public static function class(): string + { + return UserForPersistentFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +// methods returning one object +assertType('UserForPersistentFactory', UserFactory::new()->create()); +assertType('UserForPersistentFactory', UserFactory::createOne()); +assertType('UserForPersistentFactory', UserFactory::first()); +assertType('UserForPersistentFactory', UserFactory::last()); +assertType('UserForPersistentFactory', UserFactory::find(1)); +assertType('UserForPersistentFactory', UserFactory::random()); +assertType('UserForPersistentFactory', UserFactory::findOrCreate([])); +assertType('UserForPersistentFactory', UserFactory::randomOrCreate()); +assertType('UserForPersistentFactory', UserFactory::new()->instantiateWith(Instantiator::withConstructor())->create()); +assertType('UserForPersistentFactory', UserFactory::new()->with()->create()); + +// methods returning a list of objects +assertType("array", UserFactory::all()); +assertType("array", UserFactory::createMany(1)); +assertType("array", UserFactory::createRange(1, 2)); +assertType("array", UserFactory::createSequence([])); +assertType("array", UserFactory::randomRange(1, 2)); +assertType("array", UserFactory::randomSet(2)); +assertType("array", UserFactory::findBy(['name' => 'foo'])); + +// methods with FactoryCollection +$factoryCollection = FactoryCollection::class; +assertType("{$factoryCollection}", UserFactory::new()->many(2)); +assertType("{$factoryCollection}", UserFactory::new()->range(1, 2)); +assertType("{$factoryCollection}", UserFactory::new()->sequence([])); +assertType("array", UserFactory::new()->many(2)->create()); +assertType("array", UserFactory::new()->range(1, 2)->create()); +assertType("array", UserFactory::new()->sequence([])->create()); + +// methods using repository() +$repository = UserFactory::repository(); +assertType("Zenstruck\Foundry\Persistence\RepositoryDecorator>", $repository); +assertType("UserForPersistentFactory|null", $repository->first()); +assertType('UserForPersistentFactory', $repository->firstOrFail()); +assertType("UserForPersistentFactory|null", $repository->last()); +assertType('UserForPersistentFactory', $repository->lastOrFail()); +assertType("UserForPersistentFactory|null", $repository->find(1)); +assertType("UserForPersistentFactory", $repository->findOrFail(1)); +assertType("UserForPersistentFactory|null", $repository->findOneBy([])); +assertType('UserForPersistentFactory', $repository->random()); +assertType("array", $repository->findAll()); +assertType("array", $repository->findBy([])); +assertType("array", $repository->randomSet(2)); +assertType("array", $repository->randomRange(1, 2)); +assertType('int', $repository->count()); + +// test autocomplete with phpstorm +assertType('string', UserFactory::new()->create()->name); +assertType('string', UserFactory::new()->instantiateWith(Instantiator::withConstructor())->create()->name); +assertType('string', UserFactory::new()->with()->create()->name); +assertType('string', UserFactory::createOne()->name); +assertType('string', UserFactory::first()->name); +assertType('string', UserFactory::last()->name); +assertType('string', UserFactory::find(1)->name); +assertType('string', UserFactory::random()->name); +assertType('string', UserFactory::findOrCreate([])->name); +assertType('string', UserFactory::randomOrCreate()->name); + +assertType('string', proxy(UserFactory::createOne())->name); +assertType('string', proxy(UserFactory::new()->create())->name); + +assertType('string', UserFactory::all()[0]->name); +assertType("string", UserFactory::createMany(1)[0]->name); +assertType("string", UserFactory::createRange(1, 2)[0]->name); +assertType("string", UserFactory::createSequence([])[0]->name); +assertType("string", UserFactory::randomRange(1, 2)[0]->name); +assertType("string", UserFactory::randomSet(2)[0]->name); +assertType("string", UserFactory::findBy(['name' => 'foo'])[0]->name); + +assertType("string", UserFactory::new()->many(2)->create()[0]->name); +assertType("string", UserFactory::new()->range(1, 2)->create()[0]->name); +assertType("string", UserFactory::new()->sequence([])->create()[0]->name); + +assertType("string|null", $repository->first()?->name); +assertType('string', $repository->firstOrFail()->name); +assertType("string|null", $repository->last()?->name); +assertType('string', $repository->lastOrFail()->name); +assertType("string|null", $repository->find(1)?->name); +assertType("string", $repository->findOrFail(1)->name); +assertType("string|null", $repository->findOneBy([])?->name); +assertType('string', $repository->random()->name); +assertType("string", $repository->findAll()[0]->name); +assertType("string", $repository->findBy([])[0]->name); +assertType("string", $repository->randomSet(2)[0]->name); +assertType("string", $repository->randomRange(1, 2)[0]->name); diff --git a/stubs/phpstan/PersistentProxyObjectFactory.php b/stubs/phpstan/PersistentProxyObjectFactory.php new file mode 100644 index 000000000..c5768d3d5 --- /dev/null +++ b/stubs/phpstan/PersistentProxyObjectFactory.php @@ -0,0 +1,134 @@ + + */ +final class UserProxyFactory extends PersistentProxyObjectFactory +{ + public static function class(): string + { + return UserForProxyFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +$proxyType = 'UserForProxyFactory&Zenstruck\Foundry\Persistence\Proxy'; + +// methods returning one object +assertType($proxyType, UserProxyFactory::new()->create()); +assertType($proxyType, UserProxyFactory::createOne()); +assertType($proxyType, UserProxyFactory::first()); +assertType($proxyType, UserProxyFactory::last()); +assertType($proxyType, UserProxyFactory::find(1)); +assertType($proxyType, UserProxyFactory::random()); +assertType($proxyType, UserProxyFactory::findOrCreate([])); +assertType($proxyType, UserProxyFactory::randomOrCreate()); +assertType($proxyType, UserProxyFactory::new()->instantiateWith(Instantiator::withConstructor())->with()->create()); + +// methods returning a list of objects +assertType("array", UserProxyFactory::all()); +assertType("array", UserProxyFactory::createMany(1)); +assertType("array", UserProxyFactory::createRange(1, 2)); +assertType("array", UserProxyFactory::createSequence([])); +assertType("array", UserProxyFactory::randomRange(1, 2)); +assertType("array", UserProxyFactory::randomSet(2)); +assertType("array", UserProxyFactory::findBy(['name' => 'foo'])); + +// methods with FactoryCollection +$factoryCollection = FactoryCollection::class; +assertType("{$factoryCollection}<{$proxyType}>", UserProxyFactory::new()->many(2)); +assertType("{$factoryCollection}<{$proxyType}>", UserProxyFactory::new()->range(1, 2)); +assertType("{$factoryCollection}<{$proxyType}>", UserProxyFactory::new()->sequence([])); +assertType("array", UserProxyFactory::new()->many(2)->create()); +assertType("array", UserProxyFactory::new()->range(1, 2)->create()); +assertType("array", UserProxyFactory::new()->sequence([])->create()); + +// methods using repository() +$repository = UserProxyFactory::repository(); +assertType("Zenstruck\Foundry\Persistence\ProxyRepositoryDecorator>", $repository); +assertType("({$proxyType})|null", $repository->first()); +assertType($proxyType, $repository->firstOrFail()); +assertType("({$proxyType})|null", $repository->last()); +assertType($proxyType, $repository->lastOrFail()); +assertType("({$proxyType})|null", $repository->find(1)); +assertType($proxyType, $repository->findOrFail(1)); +assertType("({$proxyType})|null", $repository->findOneBy([])); +assertType($proxyType, $repository->random()); +assertType("array<{$proxyType}>", $repository->findAll()); +assertType("array<{$proxyType}>", $repository->findBy([])); +assertType("array<{$proxyType}>", $repository->randomSet(2)); +assertType("array<{$proxyType}>", $repository->randomRange(1, 2)); +assertType('int', $repository->count()); + +// check proxy methods +assertType($proxyType, UserProxyFactory::new()->create()->_refresh()); +assertType($proxyType, UserProxyFactory::createOne()->_refresh()); +assertType('UserForProxyFactory', UserProxyFactory::createOne()->_real()); + +// test autocomplete with phpstorm +assertType('string', UserProxyFactory::new()->create()->name); +assertType('string', UserProxyFactory::new()->instantiateWith(Instantiator::withConstructor())->create()->name); +assertType('string', UserProxyFactory::new()->with()->create()->name); +assertType('string', UserProxyFactory::new()->create()->_refresh()->name); +assertType('string', UserProxyFactory::new()->create()->_real()->name); // ⚠️ no auto-complete ?! +assertType('string', UserProxyFactory::createOne()->name); +assertType('string', UserProxyFactory::createOne()->_refresh()->name); +assertType('string', UserProxyFactory::createOne()->_real()->name); +assertType('string', UserProxyFactory::first()->name); +assertType('string', UserProxyFactory::first()->_refresh()->name); +assertType('string', UserProxyFactory::last()->name); +assertType('string', UserProxyFactory::find(1)->name); +assertType('string', UserProxyFactory::find(1)->_refresh()->name); +assertType('string', UserProxyFactory::random()->name); +assertType('string', UserProxyFactory::random()->_refresh()->name); +assertType('string', UserProxyFactory::findOrCreate([])->name); +assertType('string', UserProxyFactory::findOrCreate([])->_refresh()->name); +assertType('string', UserProxyFactory::randomOrCreate()->name); +assertType('string', UserProxyFactory::randomOrCreate()->_refresh()->name); + +assertType('string', unproxy(UserProxyFactory::createOne())->name); +assertType('string', unproxy(UserProxyFactory::new()->create())->name); + +assertType('string', UserProxyFactory::all()[0]->name); +assertType("string", UserProxyFactory::createMany(1)[0]->name); +assertType("string", UserProxyFactory::createRange(1, 2)[0]->name); +assertType("string", UserProxyFactory::createSequence([])[0]->name); +assertType("string", UserProxyFactory::randomRange(1, 2)[0]->name); +assertType("string", UserProxyFactory::randomSet(2)[0]->name); +assertType("string", UserProxyFactory::findBy(['name' => 'foo'])[0]->name); + +assertType("string", UserProxyFactory::new()->many(2)->create()[0]->name); +assertType("string", UserProxyFactory::new()->range(1, 2)->create()[0]->name); +assertType("string", UserProxyFactory::new()->sequence([])->create()[0]->name); + +assertType("string|null", $repository->first()?->name); +assertType('string', $repository->firstOrFail()->name); +assertType('string', $repository->firstOrFail()->_refresh()->name); +assertType("string|null", $repository->last()?->name); +assertType('string', $repository->lastOrFail()->name); +assertType("string|null", $repository->find(1)?->name); +assertType("string", $repository->findOrFail(1)->name); +assertType("string|null", $repository->findOneBy([])?->name); +assertType('string', $repository->random()->name); +assertType("string", $repository->findAll()[0]->name); +assertType("string", $repository->findBy([])[0]->name); +assertType("string", $repository->randomSet(2)[0]->name); +assertType("string", $repository->randomRange(1, 2)[0]->name); diff --git a/stubs/phpstan/functions.php b/stubs/phpstan/functions.php new file mode 100644 index 000000000..a57973e41 --- /dev/null +++ b/stubs/phpstan/functions.php @@ -0,0 +1,26 @@ +create()->name); +assertType('string', object(UserForPersistentFactory::class)->name); + +assertType('string', persistent_factory(UserForPersistentFactory::class)->create()->name); +assertType('string', persist(UserForPersistentFactory::class)->name); + +assertType('UserForPersistentFactory|null', repository(UserForPersistentFactory::class)->find(1)); + +assertType('UserForPersistentFactory&Zenstruck\Foundry\Persistence\Proxy', proxy(object(UserForPersistentFactory::class))); +assertType('string', proxy(object(UserForPersistentFactory::class))->name); +assertType('string', proxy(object(UserForPersistentFactory::class))->_refresh()->name); diff --git a/stubs/psalm/ObjectFactory.php b/stubs/psalm/ObjectFactory.php new file mode 100644 index 000000000..f51369012 --- /dev/null +++ b/stubs/psalm/ObjectFactory.php @@ -0,0 +1,63 @@ + + */ +final class UserObjectFactory extends ObjectFactory +{ + public static function class(): string + { + return UserForObjectFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +// methods returning one object +/** @psalm-check-type-exact $var = UserForObjectFactory */ +$var = UserObjectFactory::new()->create(); +/** @psalm-check-type-exact $var = UserForObjectFactory */ +$var = UserObjectFactory::createOne(); +/** @psalm-check-type-exact $var = UserForObjectFactory */ +$var = UserObjectFactory::new()->instantiateWith(Instantiator::withConstructor())->create(); +/** @psalm-check-type-exact $var = UserForObjectFactory */ +$var = UserObjectFactory::new()->with()->create(); + +// methods returning a list of objects +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::createMany(1); +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::createRange(1, 2); +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::createSequence([]); + +// methods with FactoryCollection +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserObjectFactory::new()->many(2); +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserObjectFactory::new()->range(1, 2); +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserObjectFactory::new()->sequence([]); +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::new()->many(2)->create(); +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::new()->range(1, 2)->create(); +/** @psalm-check-type-exact $var = list */ +$var = UserObjectFactory::new()->sequence([])->create(); diff --git a/stubs/psalm/PersistentObjectFactory.php b/stubs/psalm/PersistentObjectFactory.php new file mode 100644 index 000000000..8528730ee --- /dev/null +++ b/stubs/psalm/PersistentObjectFactory.php @@ -0,0 +1,114 @@ + + */ +final class UserFactory extends PersistentObjectFactory +{ + public static function class(): string + { + return UserForPersistentFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +// methods returning one object +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::new()->create(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::createOne(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::first(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::last(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::find(1); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::random(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::findOrCreate([]); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::randomOrCreate(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::new()->instantiateWith(Instantiator::withConstructor())->create(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = UserFactory::new()->with()->create(); + +// methods returning a list of objects +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::all(); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::createMany(1); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::createRange(1, 2); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::createSequence([]); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::randomRange(1, 2); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::randomSet(2); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::findBy(['name' => 'foo']); + +// methods with FactoryCollection +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserFactory::new()->many(2); +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserFactory::new()->range(1, 2); +/** @psalm-check-type-exact $var = FactoryCollection */ +$var = UserFactory::new()->sequence([]); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::new()->many(2)->create(); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::new()->range(1, 2)->create(); +/** @psalm-check-type-exact $var = list */ +$var = UserFactory::new()->sequence([])->create(); + +// methods using repository() +$repository = UserFactory::repository(); +/** @psalm-check-type-exact $var = RepositoryDecorator> */ +$var = $repository; +/** @psalm-check-type-exact $var = UserForPersistentFactory|null */ +$var = $repository->first(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = $repository->firstOrFail(); +/** @psalm-check-type-exact $var = UserForPersistentFactory|null */ +$var = $repository->last(); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = $repository->lastOrFail(); +/** @psalm-check-type-exact $var = UserForPersistentFactory|null */ +$var = $repository->find(1); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = $repository->findOrFail(1); +/** @psalm-check-type-exact $var = UserForPersistentFactory|null */ +$var = $repository->findOneBy([]); +/** @psalm-check-type-exact $var = UserForPersistentFactory */ +$var = $repository->random(); +/** @psalm-check-type-exact $var = array */ +$var = $repository->findAll(); +/** @psalm-check-type-exact $var = array */ +$var = $repository->findBy([]); +/** @psalm-check-type-exact $var = array */ +$var = $repository->randomSet(2); +/** @psalm-check-type-exact $var = array */ +$var = $repository->randomRange(1, 2); +/** @psalm-check-type-exact $var = int */ +$var = $repository->count(); diff --git a/stubs/psalm/PersistentProxyObjectFactory.php b/stubs/psalm/PersistentProxyObjectFactory.php new file mode 100644 index 000000000..10a8f4e5f --- /dev/null +++ b/stubs/psalm/PersistentProxyObjectFactory.php @@ -0,0 +1,122 @@ + + */ +final class UserProxyFactory extends PersistentProxyObjectFactory +{ + public static function class(): string + { + return UserForProxyFactory::class; + } + + protected function defaults(): array|callable + { + return []; + } +} + +// methods returning one object +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::new()->create(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::createOne(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::first(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::last(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::find(1); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::random(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::findOrCreate([]); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::randomOrCreate(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::new()->instantiateWith(Instantiator::withConstructor())->create(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::new()->with()->create(); + +// methods returning a list of objects +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::all(); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::createMany(1); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::createRange(1, 2); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::createSequence([]); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::randomRange(1, 2); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::randomSet(2); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::findBy(['name' => 'foo']); + +// methods with FactoryCollection +/** @psalm-check-type-exact $var = FactoryCollection> */ +$var = UserProxyFactory::new()->many(2); +/** @psalm-check-type-exact $var = FactoryCollection> */ +$var = UserProxyFactory::new()->range(1, 2); +/** @psalm-check-type-exact $var = FactoryCollection> */ +$var = UserProxyFactory::new()->sequence([]); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::new()->many(2)->create(); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::new()->range(1, 2)->create(); +/** @psalm-check-type-exact $var = list> */ +$var = UserProxyFactory::new()->sequence([])->create(); + +// methods using repository() +$repository = UserProxyFactory::repository(); +/** @psalm-check-type-exact $var = ProxyRepositoryDecorator> */ +$var = $repository; +/** @psalm-check-type-exact $var = (UserForProxyFactory&Proxy)|null */ +$var = $repository->first(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = $repository->firstOrFail(); +/** @psalm-check-type-exact $var = (UserForProxyFactory&Proxy)|null */ +$var = $repository->last(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = $repository->lastOrFail(); +/** @psalm-check-type-exact $var = (UserForProxyFactory&Proxy)|null */ +$var = $repository->find(1); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = $repository->findOrFail(1); +/** @psalm-check-type-exact $var = (UserForProxyFactory&Proxy)|null */ +$var = $repository->findOneBy([]); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = $repository->random(); +/** @psalm-check-type-exact $var = array> */ +$var = $repository->findAll(); +/** @psalm-check-type-exact $var = array> */ +$var = $repository->findBy([]); +/** @psalm-check-type-exact $var = array> */ +$var = $repository->randomSet(2); +/** @psalm-check-type-exact $var = array> */ +$var = $repository->randomRange(1, 2); +/** @psalm-check-type-exact $var = int */ +$var = $repository->count(); + +// check proxy methods +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::new()->create()->_refresh(); +/** @psalm-check-type-exact $var = UserForProxyFactory&Proxy */ +$var = UserProxyFactory::createOne()->_refresh(); +/** @psalm-check-type-exact $var = UserForProxyFactory */ +$var = UserProxyFactory::createOne()->_real(); diff --git a/stubs/psalm/functions.php b/stubs/psalm/functions.php new file mode 100644 index 000000000..102e3e2ca --- /dev/null +++ b/stubs/psalm/functions.php @@ -0,0 +1,39 @@ +create(); + +/** @psalm-check-type-exact $user = User */ +$user = object(User::class); + +/** @psalm-check-type-exact $user = User */ +$user = persistent_factory(User::class)->create(); + +/** @psalm-check-type-exact $user = User */ +$user = persist(User::class); + +/** @psalm-check-type-exact $user = User|null */ +$user = repository(User::class)->find(1); + +/** @psalm-check-type-exact $user = User&Zenstruck\Foundry\Persistence\Proxy */ +$user = proxy(object(User::class)); + +/** @psalm-check-type-exact $name = string */ +$name = proxy(object(User::class))->name; + +/** @psalm-check-type-exact $user = User&Zenstruck\Foundry\Persistence\Proxy */ +$user = proxy(object(User::class))->_refresh(); diff --git a/utils/psalm/FixProxyFactoryMethodsReturnType.php b/utils/psalm/FixProxyFactoryMethodsReturnType.php new file mode 100644 index 000000000..b9bb6be0f --- /dev/null +++ b/utils/psalm/FixProxyFactoryMethodsReturnType.php @@ -0,0 +1,63 @@ +getMethodId()); + + if ($event->getCodebase()->classExtends($class, PersistentProxyObjectFactory::class)) { + $templateType = $event->getCodebase()->classlikes->getStorageFor( + $class + )->template_extended_params[PersistentProxyObjectFactory::class]['T'] ?? null; + + if (!$templateType) { + return; + } + + $templateTypeAsString = $templateType->getId(); + $proxyTypeHint = "{$templateTypeAsString}&Zenstruck\\Foundry\\Persistence\\Proxy<{$templateTypeAsString}>"; + + $methodsReturningObject = ['create', 'createone', 'find', 'findorcreate', 'first', 'last', 'random', 'randomorcreate']; + if (\in_array($method, $methodsReturningObject, true)) { + $event->setReturnTypeCandidate(Type::parseString($proxyTypeHint)); + } + + $methodsReturningListOfObjects = ['all', 'createmany', 'createrange', 'createsequence', 'findby', 'randomrange', 'randomset']; + if (\in_array($method, $methodsReturningListOfObjects, true)) { + $event->setReturnTypeCandidate(Type::parseString("list<{$proxyTypeHint}>")); + } + + $methodsReturningFactoryCollection = ['many', 'range', 'sequence']; + if (\in_array($method, $methodsReturningFactoryCollection, true)) { + $factoryCollectionClass = FactoryCollection::class; + $event->setReturnTypeCandidate(Type::parseString("{$factoryCollectionClass}<{$proxyTypeHint}>")); + } + + if ($method === 'repository' + // if repository() method is overridden in userland, we should not change the return type + && str_starts_with($event->getReturnTypeCandidate()->getId(), ProxyRepositoryDecorator::class) + ) { + $repositoryDecoratorClass = ProxyRepositoryDecorator::class; + $doctrineRepositoryClass = ObjectRepository::class; + $event->setReturnTypeCandidate( + Type::parseString( + "{$repositoryDecoratorClass}<{$templateTypeAsString}, {$doctrineRepositoryClass}<$templateTypeAsString>>" + ) + ); + } + } + } +} diff --git a/utils/psalm/FoundryPlugin.php b/utils/psalm/FoundryPlugin.php new file mode 100644 index 000000000..da15803ea --- /dev/null +++ b/utils/psalm/FoundryPlugin.php @@ -0,0 +1,16 @@ +registerHooksFromClass(FixProxyFactoryMethodsReturnType::class); + } +} From fa1d527155888355df8c39d0f49be5ab9fbea3cc Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Tue, 22 Oct 2024 20:53:32 +0200 Subject: [PATCH 2/4] minor: misc fixes for sca (#705) --- src/Configuration.php | 4 ++-- src/Factory.php | 2 +- src/FactoryCollection.php | 6 +++--- src/FactoryRegistry.php | 2 +- src/LazyValue.php | 2 -- src/Maker/Factory/FactoryClassMap.php | 2 +- src/Maker/Factory/MakeFactoryData.php | 4 ++-- src/Maker/Factory/ODMDefaultPropertiesGuesser.php | 6 +++--- src/ORM/AbstractORMPersistenceStrategy.php | 2 +- src/Object/Hydrator.php | 4 ++-- src/Object/Instantiator.php | 4 ++-- src/ObjectFactory.php | 1 - src/Persistence/PersistentObjectFactory.php | 2 +- src/Persistence/ProxyGenerator.php | 8 ++++---- src/Persistence/ProxyRepositoryDecorator.php | 2 +- src/Persistence/RepositoryAssertions.php | 14 +++++++------- src/Persistence/RepositoryDecorator.php | 12 ++++++------ src/Persistence/functions.php | 4 ++-- src/Story.php | 2 +- src/StoryRegistry.php | 10 +++++----- src/Test/Factories.php | 7 +++---- src/Test/UnitTestConfig.php | 4 ++-- src/ZenstruckFoundryBundle.php | 6 +++--- tests/Integration/Maker/MakeStoryTest.php | 2 +- tests/Integration/Maker/MakerTestCase.php | 2 +- .../Mongo/EmbeddableDocumentFactoryTest.php | 2 +- .../Mongo/GenericDocumentProxyFactoryTest.php | 2 +- .../ORM/CascadeEntityFactoryRelationshipTest.php | 8 ++++---- .../ORM/EmbeddableEntityFactoryTest.php | 2 +- .../ORM/GenericEntityProxyFactoryTest.php | 2 +- .../PolymorphicEntityFactoryRelationshipTest.php | 2 +- .../ProxyCascadeEntityFactoryRelationshipTest.php | 8 ++++---- .../ORM/ProxyEntityFactoryRelationshipTest.php | 8 ++++---- .../ORM/ProxyEntityFactoryRelationshipTestCase.php | 2 +- .../ProxyGenericEntityRepositoryDecoratorTest.php | 2 +- .../ORM/StandardEntityFactoryRelationshipTest.php | 8 ++++---- .../Persistence/GenericProxyFactoryTestCase.php | 2 +- 37 files changed, 79 insertions(+), 83 deletions(-) diff --git a/src/Configuration.php b/src/Configuration.php index 0c26525a3..b880689e1 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -29,7 +29,7 @@ final class Configuration /** * @readonly * - * @var InstantiatorCallable + * @phpstan-var InstantiatorCallable */ public $instantiator; @@ -37,7 +37,7 @@ final class Configuration private static \Closure|self|null $instance = null; /** - * @param InstantiatorCallable $instantiator + * @phpstan-param InstantiatorCallable $instantiator */ public function __construct( public readonly FactoryRegistry $factories, diff --git a/src/Factory.php b/src/Factory.php index 93869470b..37d1c84bb 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -45,7 +45,7 @@ final public static function new(array|callable $attributes = []): static } try { - $factory ??= new static(); // @phpstan-ignore-line + $factory ??= new static(); // @phpstan-ignore new.static } catch (\ArgumentCountError $e) { throw new \LogicException('Factories with dependencies (services) cannot be created before foundry is booted.', previous: $e); } diff --git a/src/FactoryCollection.php b/src/FactoryCollection.php index 0b11b252f..55a2e702a 100644 --- a/src/FactoryCollection.php +++ b/src/FactoryCollection.php @@ -23,7 +23,7 @@ final class FactoryCollection implements \IteratorAggregate { /** * @param Factory $factory - * @param \Closure():iterable $items + * @phpstan-param \Closure():iterable $items */ private function __construct(public readonly Factory $factory, private \Closure $items) { @@ -55,7 +55,7 @@ public static function range(Factory $factory, int $min, int $max): self /** * @param Factory $factory - * @param iterable $items + * @phpstan-param iterable $items * @return self */ public static function sequence(Factory $factory, iterable $items): self @@ -64,7 +64,7 @@ public static function sequence(Factory $factory, iterable $items): self } /** - * @param Attributes $attributes + * @phpstan-param Attributes $attributes * * @return list */ diff --git a/src/FactoryRegistry.php b/src/FactoryRegistry.php index 05e96d53c..2a554e2e5 100644 --- a/src/FactoryRegistry.php +++ b/src/FactoryRegistry.php @@ -36,7 +36,7 @@ public function get(string $class): ?Factory { foreach ($this->factories as $factory) { if ($class === $factory::class) { - return $factory; // @phpstan-ignore-line + return $factory; // @phpstan-ignore return.type } } diff --git a/src/LazyValue.php b/src/LazyValue.php index 81899d355..b9219714e 100644 --- a/src/LazyValue.php +++ b/src/LazyValue.php @@ -13,8 +13,6 @@ /** * @author Kevin Bond - * - * @phpstan-import-type Parameters from Factory */ final class LazyValue { diff --git a/src/Maker/Factory/FactoryClassMap.php b/src/Maker/Factory/FactoryClassMap.php index 60777e49f..4a90b6e07 100644 --- a/src/Maker/Factory/FactoryClassMap.php +++ b/src/Maker/Factory/FactoryClassMap.php @@ -26,7 +26,7 @@ final class FactoryClassMap private array $classesWithFactories; /** @param \Traversable $factories */ - public function __construct(\Traversable $factories) // @phpstan-ignore-line + public function __construct(\Traversable $factories) // @phpstan-ignore missingType.generics { $this->classesWithFactories = \array_unique( \array_reduce( diff --git a/src/Maker/Factory/MakeFactoryData.php b/src/Maker/Factory/MakeFactoryData.php index 5ac8fd5a2..9fbac6973 100644 --- a/src/Maker/Factory/MakeFactoryData.php +++ b/src/Maker/Factory/MakeFactoryData.php @@ -79,7 +79,7 @@ public function getObjectShortName(): string /** * @return class-string */ - public function getFactoryClass(): string // @phpstan-ignore-line + public function getFactoryClass(): string // @phpstan-ignore missingType.generics { return $this->isPersisted() ? PersistentProxyObjectFactory::class : ObjectFactory::class; } @@ -100,7 +100,7 @@ public function getObjectFullyQualifiedClassName(): string return $this->object->getName(); } - public function getRepositoryReflectionClass(): ?\ReflectionClass // @phpstan-ignore-line + public function getRepositoryReflectionClass(): ?\ReflectionClass // @phpstan-ignore missingType.generics { return $this->repository; } diff --git a/src/Maker/Factory/ODMDefaultPropertiesGuesser.php b/src/Maker/Factory/ODMDefaultPropertiesGuesser.php index d540bad24..c972c353a 100644 --- a/src/Maker/Factory/ODMDefaultPropertiesGuesser.php +++ b/src/Maker/Factory/ODMDefaultPropertiesGuesser.php @@ -41,13 +41,13 @@ public function __invoke(SymfonyStyle $io, MakeFactoryData $makeFactoryData, Mak continue; } - $fieldName = $item['fieldName']; // @phpstan-ignore-line + $fieldName = $item['fieldName']; // @phpstan-ignore class.notFound - if (!$makeFactoryQuery->isAllFields() && $item['nullable']) { // @phpstan-ignore-line + if (!$makeFactoryQuery->isAllFields() && $item['nullable']) { // @phpstan-ignore class.notFound continue; } - $this->addDefaultValueUsingFactory($io, $makeFactoryData, $makeFactoryQuery, $fieldName, $item['targetDocument']); // @phpstan-ignore-line + $this->addDefaultValueUsingFactory($io, $makeFactoryData, $makeFactoryQuery, $fieldName, $item['targetDocument']); // @phpstan-ignore class.notFound } } diff --git a/src/ORM/AbstractORMPersistenceStrategy.php b/src/ORM/AbstractORMPersistenceStrategy.php index cbbf2a342..3591b010a 100644 --- a/src/ORM/AbstractORMPersistenceStrategy.php +++ b/src/ORM/AbstractORMPersistenceStrategy.php @@ -121,7 +121,7 @@ final public function managedNamespaces(): array private function dropAndResetDatabase(Application $application): void { foreach ($this->connections() as $connection) { - $databasePlatform = $this->registry->getConnection($connection)->getDatabasePlatform(); // @phpstan-ignore-line + $databasePlatform = $this->registry->getConnection($connection)->getDatabasePlatform(); // @phpstan-ignore method.notFound if ($databasePlatform instanceof SQLitePlatform) { // we don't need to create the sqlite database - it's created when the schema is created diff --git a/src/Object/Hydrator.php b/src/Object/Hydrator.php index dab662d53..792c2aaba 100644 --- a/src/Object/Hydrator.php +++ b/src/Object/Hydrator.php @@ -38,8 +38,8 @@ final class Hydrator /** * @template T of object * - * @param T $object - * @param Parameters $parameters + * @param T $object + * @phpstan-param Parameters $parameters * * @return T */ diff --git a/src/Object/Instantiator.php b/src/Object/Instantiator.php index 5475b6ccf..5d2b71838 100644 --- a/src/Object/Instantiator.php +++ b/src/Object/Instantiator.php @@ -36,7 +36,7 @@ private function __construct(private string|\Closure $mode) /** * @template T of object * - * @param Parameters $parameters + * @phpstan-param Parameters $parameters * @param class-string $class * * @return T @@ -115,7 +115,7 @@ public function disableHydration(): self /** * @template T of object * - * @param Parameters $parameters + * @phpstan-param Parameters $parameters * @param class-string $class * * @return T diff --git a/src/ObjectFactory.php b/src/ObjectFactory.php index f791ec89f..49ef7dc68 100644 --- a/src/ObjectFactory.php +++ b/src/ObjectFactory.php @@ -21,7 +21,6 @@ * * @phpstan-type InstantiatorCallable = Instantiator|callable(Parameters,class-string):T * @phpstan-import-type Parameters from Factory - * @phpstan-import-type Attributes from Factory */ abstract class ObjectFactory extends Factory { diff --git a/src/Persistence/PersistentObjectFactory.php b/src/Persistence/PersistentObjectFactory.php index cdc484df5..e7ca86489 100644 --- a/src/Persistence/PersistentObjectFactory.php +++ b/src/Persistence/PersistentObjectFactory.php @@ -166,7 +166,7 @@ public static function repository(): ObjectRepository { Configuration::instance()->assertPersistanceEnabled(); - return new RepositoryDecorator(static::class()); // @phpstan-ignore-line + return new RepositoryDecorator(static::class()); // @phpstan-ignore return.type } final public static function assert(): RepositoryAssertions diff --git a/src/Persistence/ProxyGenerator.php b/src/Persistence/ProxyGenerator.php index 2458426b9..27f5d9d2a 100644 --- a/src/Persistence/ProxyGenerator.php +++ b/src/Persistence/ProxyGenerator.php @@ -40,7 +40,7 @@ public static function wrap(object $object): Proxy return $object; } - return self::generateClassFor($object)::createLazyProxy(static fn() => $object); // @phpstan-ignore-line + return self::generateClassFor($object)::createLazyProxy(static fn() => $object); // @phpstan-ignore staticMethod.unresolvableReturnType } /** @@ -53,15 +53,15 @@ public static function wrap(object $object): Proxy public static function unwrap(mixed $what): mixed { if (\is_array($what)) { - return \array_map(self::unwrap(...), $what); // @phpstan-ignore-line + return \array_map(self::unwrap(...), $what); // @phpstan-ignore return.type } if (\is_string($what) && \is_a($what, Proxy::class, true)) { - return \get_parent_class($what) ?: throw new \LogicException('Could not unwrap proxy.'); // @phpstan-ignore-line + return \get_parent_class($what) ?: throw new \LogicException('Could not unwrap proxy.'); // @phpstan-ignore return.type } if ($what instanceof Proxy) { - return $what->_real(); // @phpstan-ignore-line + return $what->_real(); // @phpstan-ignore return.type } return $what; diff --git a/src/Persistence/ProxyRepositoryDecorator.php b/src/Persistence/ProxyRepositoryDecorator.php index 5e2d26787..666cb98af 100644 --- a/src/Persistence/ProxyRepositoryDecorator.php +++ b/src/Persistence/ProxyRepositoryDecorator.php @@ -20,7 +20,7 @@ * @template I of ObjectRepository * @extends RepositoryDecorator, I> */ -final class ProxyRepositoryDecorator extends RepositoryDecorator // @phpstan-ignore-line +final class ProxyRepositoryDecorator extends RepositoryDecorator // @phpstan-ignore class.extendsFinalByPhpDoc { /** * @return T|Proxy|null diff --git a/src/Persistence/RepositoryAssertions.php b/src/Persistence/RepositoryAssertions.php index 586766293..b3afc3cf4 100644 --- a/src/Persistence/RepositoryAssertions.php +++ b/src/Persistence/RepositoryAssertions.php @@ -32,7 +32,7 @@ public function __construct(private RepositoryDecorator $repository) } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function empty(array $criteria = [], string $message = 'Expected {entity} repository to be empty but it has {actual} items.'): self { @@ -40,7 +40,7 @@ public function empty(array $criteria = [], string $message = 'Expected {entity} } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function notEmpty(array $criteria = [], string $message = 'Expected {entity} repository to NOT be empty but it is.'): self { @@ -52,7 +52,7 @@ public function notEmpty(array $criteria = [], string $message = 'Expected {enti } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function count(int $expectedCount, array $criteria = [], string $message = 'Expected count of {entity} to be {expected} (actual: {actual}).'): self { @@ -64,7 +64,7 @@ public function count(int $expectedCount, array $criteria = [], string $message } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function countGreaterThan(int $expected, array $criteria = [], string $message = 'Expected count of {entity} to be greater than {expected} (actual: {actual}).'): self { @@ -76,7 +76,7 @@ public function countGreaterThan(int $expected, array $criteria = [], string $me } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function countGreaterThanOrEqual(int $expected, array $criteria = [], string $message = 'Expected count of {entity} to be greater than or equal {expected} (actual: {actual}).'): self { @@ -88,7 +88,7 @@ public function countGreaterThanOrEqual(int $expected, array $criteria = [], str } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function countLessThan(int $expected, array $criteria = [], string $message = 'Expected count of {entity} to be less than {expected} (actual: {actual}).'): self { @@ -100,7 +100,7 @@ public function countLessThan(int $expected, array $criteria = [], string $messa } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function countLessThanOrEqual(int $expected, array $criteria = [], string $message = 'Expected count of {entity} to be less than or equal {expected} (actual: {actual}).'): self { diff --git a/src/Persistence/RepositoryDecorator.php b/src/Persistence/RepositoryDecorator.php index 805d99002..a64ca53bf 100644 --- a/src/Persistence/RepositoryDecorator.php +++ b/src/Persistence/RepositoryDecorator.php @@ -145,7 +145,7 @@ public function getClassName(): string } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria */ public function count(array $criteria = []): int { @@ -165,7 +165,7 @@ public function truncate(): void } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T */ @@ -176,7 +176,7 @@ public function random(array $criteria = []): object /** * @param positive-int $count - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T[] */ @@ -192,7 +192,7 @@ public function randomSet(int $count, array $criteria = []): array /** * @param int<0, max> $min * @param int<0, max> $max - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return T[] */ @@ -214,7 +214,7 @@ public function randomRange(int $min, int $max, array $criteria = []): array throw new NotEnoughObjects(\sprintf('At least %d "%s" object(s) must have been persisted (%d persisted).', $max, $this->getClassName(), \count($all))); } - return \array_slice($all, 0, \random_int($min, $max)); // @phpstan-ignore-line + return \array_slice($all, 0, \random_int($min, $max)); // @phpstan-ignore argument.type } public function getIterator(): \Traversable @@ -235,7 +235,7 @@ private function inner(): ObjectRepository } /** - * @param Parameters $criteria + * @phpstan-param Parameters $criteria * * @return Parameters */ diff --git a/src/Persistence/functions.php b/src/Persistence/functions.php index 364c81236..99087678b 100644 --- a/src/Persistence/functions.php +++ b/src/Persistence/functions.php @@ -24,7 +24,7 @@ */ function repository(string $class): RepositoryDecorator { - return new RepositoryDecorator($class); // @phpstan-ignore-line + return new RepositoryDecorator($class); // @phpstan-ignore return.type } /** @@ -36,7 +36,7 @@ function repository(string $class): RepositoryDecorator */ function proxy_repository(string $class): ProxyRepositoryDecorator { - return new ProxyRepositoryDecorator($class); // @phpstan-ignore-line + return new ProxyRepositoryDecorator($class); // @phpstan-ignore return.type, argument.type } /** diff --git a/src/Story.php b/src/Story.php index 7e3c5880a..a0ad80e09 100644 --- a/src/Story.php +++ b/src/Story.php @@ -104,7 +104,7 @@ final public static function getRandomRange(string $pool, int $min, int $max): a throw new \RuntimeException(\sprintf('At least %d items must be in pool "%s" (%d items found).', $max, $pool, \count($values))); } - return \array_slice($values, 0, \random_int($min, $max)); // @phpstan-ignore-line + return \array_slice($values, 0, \random_int($min, $max)); // @phpstan-ignore argument.type } final public static function load(): static diff --git a/src/StoryRegistry.php b/src/StoryRegistry.php index e899a25c3..cb07db593 100644 --- a/src/StoryRegistry.php +++ b/src/StoryRegistry.php @@ -42,11 +42,11 @@ public function __construct(private iterable $stories, private array $globalStor public function load(string $class): Story { if (\array_key_exists($class, self::$globalInstances)) { - return self::$globalInstances[$class]; // @phpstan-ignore-line + return self::$globalInstances[$class]; // @phpstan-ignore return.type } if (\array_key_exists($class, self::$instances)) { - return self::$instances[$class]; // @phpstan-ignore-line + return self::$instances[$class]; // @phpstan-ignore return.type } self::$instances[$class] = $this->getOrCreateStory($class); @@ -60,7 +60,7 @@ public function loadGlobalStories(): void self::$globalInstances = []; foreach ($this->globalStories as $story) { - \is_a($story, Story::class, true) ? $this->load($story) : $story(); // @phpstan-ignore-line + \is_a($story, Story::class, true) ? $this->load($story) : $story(); // @phpstan-ignore argument.type, argument.type } self::$globalInstances = self::$instances; @@ -83,13 +83,13 @@ private function getOrCreateStory(string $class): Story { foreach ($this->stories as $story) { if ($class === $story::class) { - return $story; // @phpstan-ignore-line + return $story; // @phpstan-ignore return.type } } try { return new $class(); - } catch (\ArgumentCountError $e) { // @phpstan-ignore-line + } catch (\ArgumentCountError $e) { // @phpstan-ignore catch.neverThrown throw new \RuntimeException('Stories with dependencies (Story services) cannot be used without the foundry bundle.', 0, $e); } } diff --git a/src/Test/Factories.php b/src/Test/Factories.php index 7c22831b3..e75ac193c 100644 --- a/src/Test/Factories.php +++ b/src/Test/Factories.php @@ -28,7 +28,7 @@ trait Factories #[Before] public static function _bootFoundry(): void { - if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line + if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore function.impossibleType // unit test Configuration::boot(UnitTestConfig::build()); @@ -36,13 +36,12 @@ public static function _bootFoundry(): void } // integration test - // @phpstan-ignore-next-line Configuration::boot(static function() { - if (!static::getContainer()->has('.zenstruck_foundry.configuration')) { // @phpstan-ignore-line + if (!static::getContainer()->has('.zenstruck_foundry.configuration')) { // @phpstan-ignore staticMethod.notFound throw new \LogicException('ZenstruckFoundryBundle is not enabled. Ensure it is added to your config/bundles.php.'); } - return static::getContainer()->get('.zenstruck_foundry.configuration'); // @phpstan-ignore-line + return static::getContainer()->get('.zenstruck_foundry.configuration'); // @phpstan-ignore staticMethod.notFound }); } diff --git a/src/Test/UnitTestConfig.php b/src/Test/UnitTestConfig.php index 86016f28e..145a73021 100644 --- a/src/Test/UnitTestConfig.php +++ b/src/Test/UnitTestConfig.php @@ -25,12 +25,12 @@ */ final class UnitTestConfig { - /** @var InstantiatorCallable|null */ + /** @phpstan-var InstantiatorCallable|null */ private static $instantiator; private static ?Faker\Generator $faker = null; /** - * @param InstantiatorCallable|null $instantiator + * @phpstan-param InstantiatorCallable|null $instantiator */ public static function configure(Instantiator|callable|null $instantiator = null, ?Faker\Generator $faker = null): void { diff --git a/src/ZenstruckFoundryBundle.php b/src/ZenstruckFoundryBundle.php index 903c8172d..2ce4ab13b 100644 --- a/src/ZenstruckFoundryBundle.php +++ b/src/ZenstruckFoundryBundle.php @@ -28,13 +28,13 @@ final class ZenstruckFoundryBundle extends AbstractBundle implements CompilerPas public function boot(): void { if ($this->container && !Configuration::isBooted()) { - Configuration::boot($this->container->get('.zenstruck_foundry.configuration')); // @phpstan-ignore-line + Configuration::boot($this->container->get('.zenstruck_foundry.configuration')); // @phpstan-ignore argument.type } } public function configure(DefinitionConfigurator $definition): void { - $definition->rootNode() // @phpstan-ignore-line + $definition->rootNode() // @phpstan-ignore method.notFound ->children() ->booleanNode('auto_refresh_proxies') ->info('Whether to auto-refresh proxies by default (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#auto-refresh)') @@ -184,7 +184,7 @@ public function configure(DefinitionConfigurator $definition): void ; } - public function loadExtension(array $config, ContainerConfigurator $configurator, ContainerBuilder $container): void // @phpstan-ignore-line + public function loadExtension(array $config, ContainerConfigurator $configurator, ContainerBuilder $container): void // @phpstan-ignore missingType.iterableValue { $container->registerForAutoconfiguration(Factory::class) ->addTag('foundry.factory') diff --git a/tests/Integration/Maker/MakeStoryTest.php b/tests/Integration/Maker/MakeStoryTest.php index 7b5ef818f..072bc932c 100644 --- a/tests/Integration/Maker/MakeStoryTest.php +++ b/tests/Integration/Maker/MakeStoryTest.php @@ -220,7 +220,7 @@ public function can_customize_namespace(string $filePath, array $commandOptions, $tester->execute(['name' => 'FooBar'] + $commandOptions); $this->assertFileExists(self::tempFile($filePath)); - self::assertStringContainsString("namespace {$expectedFullNamespace}", \file_get_contents(self::tempFile($filePath))); // @phpstan-ignore-line + self::assertStringContainsString("namespace {$expectedFullNamespace}", \file_get_contents(self::tempFile($filePath))); // @phpstan-ignore argument.type } /** diff --git a/tests/Integration/Maker/MakerTestCase.php b/tests/Integration/Maker/MakerTestCase.php index 3e0c389dd..b2c974cf0 100644 --- a/tests/Integration/Maker/MakerTestCase.php +++ b/tests/Integration/Maker/MakerTestCase.php @@ -51,7 +51,7 @@ protected function expectedFile(): string $this->assertFileExists($path); - return \realpath($path); // @phpstan-ignore-line + return \realpath($path); // @phpstan-ignore return.type } protected function assertFileFromMakerSameAsExpectedFile(string $fileFromMaker): void diff --git a/tests/Integration/Mongo/EmbeddableDocumentFactoryTest.php b/tests/Integration/Mongo/EmbeddableDocumentFactoryTest.php index 90b396c31..ef036ea3e 100644 --- a/tests/Integration/Mongo/EmbeddableDocumentFactoryTest.php +++ b/tests/Integration/Mongo/EmbeddableDocumentFactoryTest.php @@ -55,6 +55,6 @@ public function embed_many(): void protected function withEmbeddableFactory(): PersistentObjectFactory { - return persistent_factory(WithEmbeddableDocument::class); // @phpstan-ignore-line + return persistent_factory(WithEmbeddableDocument::class); // @phpstan-ignore return.type } } diff --git a/tests/Integration/Mongo/GenericDocumentProxyFactoryTest.php b/tests/Integration/Mongo/GenericDocumentProxyFactoryTest.php index a3c581e21..f3e623657 100644 --- a/tests/Integration/Mongo/GenericDocumentProxyFactoryTest.php +++ b/tests/Integration/Mongo/GenericDocumentProxyFactoryTest.php @@ -34,7 +34,7 @@ protected function factory(): PersistentProxyObjectFactory /** * @return PersistentProxyObjectFactory */ - protected function objectWithReadonlyFactory(): PersistentProxyObjectFactory // @phpstan-ignore-line + protected function objectWithReadonlyFactory(): PersistentProxyObjectFactory // @phpstan-ignore method.childReturnType { return proxy_factory(DocumentWithReadonly::class); } diff --git a/tests/Integration/ORM/CascadeEntityFactoryRelationshipTest.php b/tests/Integration/ORM/CascadeEntityFactoryRelationshipTest.php index c66bb7d22..ec00ef40f 100644 --- a/tests/Integration/ORM/CascadeEntityFactoryRelationshipTest.php +++ b/tests/Integration/ORM/CascadeEntityFactoryRelationshipTest.php @@ -96,21 +96,21 @@ public function ensure_one_to_many_cascade_relations_are_not_pre_persisted(): vo protected function contactFactory(): PersistentObjectFactory { - return CascadeContactFactory::new(); // @phpstan-ignore-line + return CascadeContactFactory::new(); // @phpstan-ignore return.type } protected function categoryFactory(): PersistentObjectFactory { - return CascadeCategoryFactory::new(); // @phpstan-ignore-line + return CascadeCategoryFactory::new(); // @phpstan-ignore return.type } protected function tagFactory(): PersistentObjectFactory { - return CascadeTagFactory::new(); // @phpstan-ignore-line + return CascadeTagFactory::new(); // @phpstan-ignore return.type } protected function addressFactory(): PersistentObjectFactory { - return CascadeAddressFactory::new(); // @phpstan-ignore-line + return CascadeAddressFactory::new(); // @phpstan-ignore return.type } } diff --git a/tests/Integration/ORM/EmbeddableEntityFactoryTest.php b/tests/Integration/ORM/EmbeddableEntityFactoryTest.php index 7a28e9b14..258dea8ff 100644 --- a/tests/Integration/ORM/EmbeddableEntityFactoryTest.php +++ b/tests/Integration/ORM/EmbeddableEntityFactoryTest.php @@ -27,6 +27,6 @@ final class EmbeddableEntityFactoryTest extends EmbeddableFactoryTestCase protected function withEmbeddableFactory(): PersistentObjectFactory { - return persistent_factory(WithEmbeddableEntity::class); // @phpstan-ignore-line + return persistent_factory(WithEmbeddableEntity::class); // @phpstan-ignore return.type } } diff --git a/tests/Integration/ORM/GenericEntityProxyFactoryTest.php b/tests/Integration/ORM/GenericEntityProxyFactoryTest.php index 218c7b518..7cf8a82c9 100644 --- a/tests/Integration/ORM/GenericEntityProxyFactoryTest.php +++ b/tests/Integration/ORM/GenericEntityProxyFactoryTest.php @@ -34,7 +34,7 @@ protected function factory(): PersistentProxyObjectFactory /** * @return PersistentProxyObjectFactory */ - protected function objectWithReadonlyFactory(): PersistentProxyObjectFactory // @phpstan-ignore-line + protected function objectWithReadonlyFactory(): PersistentProxyObjectFactory // @phpstan-ignore method.childReturnType { return proxy_factory(EntityWithReadonly::class); } diff --git a/tests/Integration/ORM/PolymorphicEntityFactoryRelationshipTest.php b/tests/Integration/ORM/PolymorphicEntityFactoryRelationshipTest.php index fe075b387..92607a6b7 100644 --- a/tests/Integration/ORM/PolymorphicEntityFactoryRelationshipTest.php +++ b/tests/Integration/ORM/PolymorphicEntityFactoryRelationshipTest.php @@ -21,6 +21,6 @@ class PolymorphicEntityFactoryRelationshipTest extends StandardEntityFactoryRela { protected function contactFactory(): PersistentObjectFactory { - return ChildContactFactory::new(); // @phpstan-ignore-line + return ChildContactFactory::new(); // @phpstan-ignore return.type } } diff --git a/tests/Integration/ORM/ProxyCascadeEntityFactoryRelationshipTest.php b/tests/Integration/ORM/ProxyCascadeEntityFactoryRelationshipTest.php index d05905070..e466a2465 100644 --- a/tests/Integration/ORM/ProxyCascadeEntityFactoryRelationshipTest.php +++ b/tests/Integration/ORM/ProxyCascadeEntityFactoryRelationshipTest.php @@ -24,21 +24,21 @@ final class ProxyCascadeEntityFactoryRelationshipTest extends ProxyEntityFactory { protected function contactFactory(): PersistentObjectFactory { - return ProxyCascadeContactFactory::new(); // @phpstan-ignore-line + return ProxyCascadeContactFactory::new(); // @phpstan-ignore return.type } protected function categoryFactory(): PersistentObjectFactory { - return ProxyCascadeCategoryFactory::new(); // @phpstan-ignore-line + return ProxyCascadeCategoryFactory::new(); // @phpstan-ignore return.type } protected function tagFactory(): PersistentObjectFactory { - return ProxyCascadeTagFactory::new(); // @phpstan-ignore-line + return ProxyCascadeTagFactory::new(); // @phpstan-ignore return.type } protected function addressFactory(): PersistentObjectFactory { - return ProxyCascadeAddressFactory::new(); // @phpstan-ignore-line + return ProxyCascadeAddressFactory::new(); // @phpstan-ignore return.type } } diff --git a/tests/Integration/ORM/ProxyEntityFactoryRelationshipTest.php b/tests/Integration/ORM/ProxyEntityFactoryRelationshipTest.php index c8b075c71..a56852831 100644 --- a/tests/Integration/ORM/ProxyEntityFactoryRelationshipTest.php +++ b/tests/Integration/ORM/ProxyEntityFactoryRelationshipTest.php @@ -24,21 +24,21 @@ final class ProxyEntityFactoryRelationshipTest extends ProxyEntityFactoryRelatio { protected function contactFactory(): PersistentObjectFactory { - return ProxyContactFactory::new(); // @phpstan-ignore-line + return ProxyContactFactory::new(); // @phpstan-ignore return.type } protected function categoryFactory(): PersistentObjectFactory { - return ProxyCategoryFactory::new(); // @phpstan-ignore-line + return ProxyCategoryFactory::new(); // @phpstan-ignore return.type } protected function tagFactory(): PersistentObjectFactory { - return ProxyTagFactory::new(); // @phpstan-ignore-line + return ProxyTagFactory::new(); // @phpstan-ignore return.type } protected function addressFactory(): PersistentObjectFactory { - return ProxyAddressFactory::new(); // @phpstan-ignore-line + return ProxyAddressFactory::new(); // @phpstan-ignore return.type } } diff --git a/tests/Integration/ORM/ProxyEntityFactoryRelationshipTestCase.php b/tests/Integration/ORM/ProxyEntityFactoryRelationshipTestCase.php index 07b987c39..400d777bc 100644 --- a/tests/Integration/ORM/ProxyEntityFactoryRelationshipTestCase.php +++ b/tests/Integration/ORM/ProxyEntityFactoryRelationshipTestCase.php @@ -43,7 +43,7 @@ public function doctrine_proxies_are_converted_to_foundry_proxies(): void $this->contactFactory()->create(['category' => $this->categoryFactory()]); // clear the em so nothing is tracked - self::getContainer()->get(EntityManagerInterface::class)->clear(); // @phpstan-ignore-line + self::getContainer()->get(EntityManagerInterface::class)->clear(); // @phpstan-ignore method.nonObject // load a random Contact which causes the em to track a "doctrine proxy" for category $this->contactFactory()::random(); diff --git a/tests/Integration/ORM/ProxyGenericEntityRepositoryDecoratorTest.php b/tests/Integration/ORM/ProxyGenericEntityRepositoryDecoratorTest.php index 23266429a..844ea31a7 100644 --- a/tests/Integration/ORM/ProxyGenericEntityRepositoryDecoratorTest.php +++ b/tests/Integration/ORM/ProxyGenericEntityRepositoryDecoratorTest.php @@ -21,7 +21,7 @@ final class ProxyGenericEntityRepositoryDecoratorTest extends GenericRepositoryD { use RequiresORM; - protected function factory(): GenericProxyEntityFactory // @phpstan-ignore-line + protected function factory(): GenericProxyEntityFactory // @phpstan-ignore method.childReturnType { return GenericProxyEntityFactory::new(); } diff --git a/tests/Integration/ORM/StandardEntityFactoryRelationshipTest.php b/tests/Integration/ORM/StandardEntityFactoryRelationshipTest.php index 19897d0ef..5189be1cf 100644 --- a/tests/Integration/ORM/StandardEntityFactoryRelationshipTest.php +++ b/tests/Integration/ORM/StandardEntityFactoryRelationshipTest.php @@ -24,21 +24,21 @@ class StandardEntityFactoryRelationshipTest extends EntityFactoryRelationshipTes { protected function contactFactory(): PersistentObjectFactory { - return StandardContactFactory::new(); // @phpstan-ignore-line + return StandardContactFactory::new(); // @phpstan-ignore return.type } protected function categoryFactory(): PersistentObjectFactory { - return StandardCategoryFactory::new(); // @phpstan-ignore-line + return StandardCategoryFactory::new(); // @phpstan-ignore return.type } protected function tagFactory(): PersistentObjectFactory { - return StandardTagFactory::new(); // @phpstan-ignore-line + return StandardTagFactory::new(); // @phpstan-ignore return.type } protected function addressFactory(): PersistentObjectFactory { - return StandardAddressFactory::new(); // @phpstan-ignore-line + return StandardAddressFactory::new(); // @phpstan-ignore return.type } } diff --git a/tests/Integration/Persistence/GenericProxyFactoryTestCase.php b/tests/Integration/Persistence/GenericProxyFactoryTestCase.php index e95976388..6728a18c4 100644 --- a/tests/Integration/Persistence/GenericProxyFactoryTestCase.php +++ b/tests/Integration/Persistence/GenericProxyFactoryTestCase.php @@ -283,7 +283,7 @@ public function can_use_after_persist_with_attributes(): void /** * @return PersistentProxyObjectFactory */ - abstract protected function factory(): PersistentProxyObjectFactory; // @phpstan-ignore-line + abstract protected function factory(): PersistentProxyObjectFactory; // @phpstan-ignore method.childReturnType /** * @return PersistentProxyObjectFactory From 605f105a9f0bc6dfbd10285333d37f04f603e311 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 18 Oct 2024 15:22:54 +0200 Subject: [PATCH 3/4] docs: document new option orm.migrations.configurations --- docs/index.rst | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 3e5a0da0a..5d222c982 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1248,20 +1248,14 @@ this for you. Before the first test using the ``ResetDatabase`` trait, it drops (if exists) and creates the test database. Then, by default, before each test, it resets the schema using ``doctrine:schema:drop``/``doctrine:schema:create``. -Alternatively, you can have it run your migrations instead by modifying the ``reset_mode`` option in configuration file. -When using this *mode*, before each test, the database is dropped/created and your migrations run (via -``doctrine:migrations:migrate``). This mode can really make your test suite slow (especially if you have a lot of -migrations). It is highly recommended to use `DamaDoctrineTestBundle`_ to improve the -speed. When this bundle is enabled, the database is dropped/created and migrated only once for the suite. - .. tip:: Create a base TestCase for tests using factories to avoid adding the traits to every TestCase. .. tip:: - If your tests :ref:`are not persisting ` the objects they create, these test traits are not - required. + If your tests :ref:`are not persisting ` the objects they create, the ``ResetDatabase`` + trait is not required. By default, ``ResetDatabase`` resets the default configured connection's database and default configured object manager's schema. To customize the connection's and object manager's to be reset (or reset multiple connections/managers), use the @@ -1282,12 +1276,43 @@ bundle's configuration: object_managers: - orm_object_manager_1 - orm_object_manager_2 - reset_mode: schema + reset_mode: schema # default value, enables resetting the schema with doctrine:schema commands mongo: object_managers: - odm_object_manager_1 - odm_object_manager_2 +Resetting using migrations +.......................... + +Alternatively, you can have it run your migrations instead by modifying the ``orm.reset.mode`` option in configuration file. +When using this *mode*, before each test, the database is dropped/created and your migrations run (via +``doctrine:migrations:migrate``). This mode can really make your test suite slow (especially if you have a lot of +migrations). It is highly recommended to use `DamaDoctrineTestBundle`_ to improve the +speed. When this bundle is enabled, the database is dropped/created and migrated only once for the suite. + +Additionally, it is possible to provide `configuration files `_ +to be used by the migrations. The configuration files can be in any format supported by Doctrine Migrations (php, xml, +json, yml). Then then command ``doctrine:migrations:migrate`` will run as many times as the number of configuration +files. + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/zenstruck_foundry.yaml + when@dev: # see Bundle Configuration section about sharing this in the test environment + zenstruck_foundry: + database_resetter: + orm: + reset_mode: migrate # enables resetting with migrations + + # optional: allows you to pass additional configuration to the doctrine:migrations:migrate command + migrations: + configurations: + - '%kernel.root_dir%/migrations/configuration.php' + - 'migrations/configuration.yaml' + .. _object-proxy: Object Proxy @@ -2102,6 +2127,11 @@ Full Default Bundle Configuration # Reset mode to use with ResetDatabase trait mode: schema # One of "schema"; "migrate" + migrations: + + # Migration configurations + configurations: [] + mongo: reset: From ac54a3f0cc7b8814643215a88036a5fd9756bb45 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Fri, 18 Oct 2024 15:34:30 +0200 Subject: [PATCH 4/4] docs: add some doc for the instantiator --- docs/index.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index 5d222c982..9944b4b30 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -648,6 +648,14 @@ You can customize the instantiator in several ways: // can combine the different "modes" ->instantiateWith(Instantiator::withoutConstructor()->allowExtra()->alwaysForce()) + // use a "namedConstructor" + ->instantiateWith(Instantiator::namedConstructor("methodName")) + + // use a callable + ->instantiateWith(Instantiator::use(function(string $title): object { + return new Post($title); // ... your own logic + })) + // the instantiator is just a callable, you can provide your own ->instantiateWith(function(array $attributes, string $class): object { return new Post(); // ... your own logic