diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index a545c1b3..943c0957 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -17,6 +17,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" dependencies: - "lowest" - "highest" diff --git a/composer.json b/composer.json index 0baa4051..e5c9c52f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ ], "bin": ["bin/svc"], "require": { - "php": "~8.1.0||~8.2.0||~8.3.0", + "php": "~8.1.0||~8.2.0||~8.3.0||~8.4.0", "ext-json": "*", "laminas/laminas-stdlib": "^3.18", "nikic/php-parser": "^4.15", diff --git a/composer.lock b/composer.lock index 1bce5fca..d97f6e3f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "33ac8c8057e26605bdd59f9d6162b506", + "content-hash": "fef25ea90a323b619f8947668fa74553", "packages": [ { "name": "hassankhan/config", @@ -70,30 +70,30 @@ }, { "name": "laminas/laminas-stdlib", - "version": "3.19.0", + "version": "3.20.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-stdlib.git", - "reference": "6a192dd0882b514e45506f533b833b623b78fff3" + "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/6a192dd0882b514e45506f533b833b623b78fff3", - "reference": "6a192dd0882b514e45506f533b833b623b78fff3", + "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/8974a1213be42c3e2f70b2c27b17f910291ab2f4", + "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "conflict": { "zendframework/zend-stdlib": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^2.5", - "phpbench/phpbench": "^1.2.15", - "phpunit/phpunit": "^10.5.8", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.20.0" + "laminas/laminas-coding-standard": "^3.0", + "phpbench/phpbench": "^1.3.1", + "phpunit/phpunit": "^10.5.38", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.26.1" }, "type": "library", "autoload": { @@ -125,20 +125,20 @@ "type": "community_bridge" } ], - "time": "2024-01-19T12:39:49+00:00" + "time": "2024-10-29T13:46:07+00:00" }, { "name": "nikic/php-parser", - "version": "v4.19.1", + "version": "v4.19.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", "shasum": "" }, "require": { @@ -147,7 +147,7 @@ }, "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" @@ -179,9 +179,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" }, - "time": "2024-03-17T08:10:35+00:00" + "time": "2024-09-29T15:01:53+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -287,27 +287,27 @@ }, { "name": "sabre/uri", - "version": "2.3.3", + "version": "2.3.4", "source": { "type": "git", "url": "https://github.com/sabre-io/uri.git", - "reference": "7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b" + "reference": "b76524c22de90d80ca73143680a8e77b1266c291" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/uri/zipball/7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b", - "reference": "7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b", + "url": "https://api.github.com/repos/sabre-io/uri/zipball/b76524c22de90d80ca73143680a8e77b1266c291", + "reference": "b76524c22de90d80ca73143680a8e77b1266c291", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.17", - "phpstan/extension-installer": "^1.3", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-phpunit": "^1.3", - "phpstan/phpstan-strict-rules": "^1.5", + "friendsofphp/php-cs-fixer": "^3.63", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-phpunit": "^1.4", + "phpstan/phpstan-strict-rules": "^1.6", "phpunit/phpunit": "^9.6" }, "type": "library", @@ -343,20 +343,20 @@ "issues": "https://github.com/sabre-io/uri/issues", "source": "https://github.com/fruux/sabre-uri" }, - "time": "2023-06-09T06:54:04+00:00" + "time": "2024-08-27T12:18:16+00:00" }, { "name": "sabre/xml", - "version": "2.2.6", + "version": "2.2.11", "source": { "type": "git", "url": "https://github.com/sabre-io/xml.git", - "reference": "9cde7cdab1e50893cc83b037b40cd47bfde42a2b" + "reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/xml/zipball/9cde7cdab1e50893cc83b037b40cd47bfde42a2b", - "reference": "9cde7cdab1e50893cc83b037b40cd47bfde42a2b", + "url": "https://api.github.com/repos/sabre-io/xml/zipball/01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc", + "reference": "01a7927842abf3e10df3d9c2d9b0cc9d813a3fcc", "shasum": "" }, "require": { @@ -368,9 +368,9 @@ "sabre/uri": ">=1.0,<3.0.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1", + "friendsofphp/php-cs-fixer": "~2.17.1||3.63.2", "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.0" + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, "type": "library", "autoload": { @@ -412,20 +412,20 @@ "issues": "https://github.com/sabre-io/xml/issues", "source": "https://github.com/fruux/sabre-xml" }, - "time": "2023-06-28T12:56:05+00:00" + "time": "2024-09-06T07:37:46+00:00" }, { "name": "symfony/console", - "version": "v6.4.3", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e" + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e", - "reference": "2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e", + "url": "https://api.github.com/repos/symfony/console/zipball/f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", + "reference": "f1fc6f47283e27336e7cebb9e8946c8de7bff9bd", "shasum": "" }, "require": { @@ -490,7 +490,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.3" + "source": "https://github.com/symfony/console/tree/v6.4.15" }, "funding": [ { @@ -506,20 +506,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-11-06T14:19:14+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "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/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -528,7 +528,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -557,7 +557,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.1" }, "funding": [ { @@ -573,24 +573,24 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -636,7 +636,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -652,24 +652,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "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" @@ -714,7 +714,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -730,24 +730,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "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" @@ -795,7 +795,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -811,24 +811,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -875,7 +875,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -891,25 +891,26 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.1", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^1.1|^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -917,7 +918,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -957,7 +958,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -973,24 +974,24 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v6.4.3", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "7a14736fb179876575464e4658fce0c304e8c15b" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/7a14736fb179876575464e4658fce0c304e8c15b", - "reference": "7a14736fb179876575464e4658fce0c304e8c15b", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "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", @@ -1000,11 +1001,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": { @@ -1043,7 +1045,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.3" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -1059,20 +1061,20 @@ "type": "tidelift" } ], - "time": "2024-01-25T09:26:29+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.3", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d75715985f0f94f978e3a8fa42533e10db921b90" + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d75715985f0f94f978e3a8fa42533e10db921b90", - "reference": "d75715985f0f94f978e3a8fa42533e10db921b90", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", + "reference": "e99b4e94d124b29ee4cf3140e1b537d2dad8cec9", "shasum": "" }, "require": { @@ -1115,7 +1117,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.3" + "source": "https://github.com/symfony/yaml/tree/v6.4.13" }, "funding": [ { @@ -1131,7 +1133,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "tomzx/finder", @@ -1329,16 +1331,16 @@ "packages-dev": [ { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -1346,11 +1348,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -1376,7 +1379,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -1384,7 +1387,7 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "phar-io/manifest", @@ -1506,32 +1509,32 @@ }, { "name": "phpunit/php-code-coverage", - "version": "10.1.14", + "version": "10.1.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { "phpunit/phpunit": "^10.1" @@ -1543,7 +1546,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.1.x-dev" } }, "autoload": { @@ -1572,7 +1575,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, "funding": [ { @@ -1580,7 +1583,7 @@ "type": "github" } ], - "time": "2024-03-12T15:33:41+00:00" + "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", @@ -1827,16 +1830,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.19", + "version": "10.5.38", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c726f0de022368f6ed103e452a765d3304a996a4" + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c726f0de022368f6ed103e452a765d3304a996a4", - "reference": "c726f0de022368f6ed103e452a765d3304a996a4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a86773b9e887a67bc53efa9da9ad6e3f2498c132", + "reference": "a86773b9e887a67bc53efa9da9ad6e3f2498c132", "shasum": "" }, "require": { @@ -1846,26 +1849,26 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.3", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -1908,7 +1911,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.38" }, "funding": [ { @@ -1924,7 +1927,7 @@ "type": "tidelift" } ], - "time": "2024-04-17T14:06:18+00:00" + "time": "2024-10-28T13:06:21+00:00" }, { "name": "sebastian/cli-parser", @@ -2096,16 +2099,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { @@ -2116,7 +2119,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -2161,7 +2164,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -2169,7 +2172,7 @@ "type": "github" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { "name": "sebastian/complexity", @@ -2965,15 +2968,15 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.1.0||~8.2.0||~8.3.0", + "php": "~8.1.0||~8.2.0||~8.3.0||~8.4.0", "ext-json": "*" }, "platform-dev": { "ext-dom": "*" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.6.0" } diff --git a/dev/tests/Unit/Console/Command/CompareSourceCommandApiClassesTest.php b/dev/tests/Unit/Console/Command/CompareSourceCommandApiClassesTest.php index 82d4ed6c..32dadfe9 100644 --- a/dev/tests/Unit/Console/Command/CompareSourceCommandApiClassesTest.php +++ b/dev/tests/Unit/Console/Command/CompareSourceCommandApiClassesTest.php @@ -59,10 +59,10 @@ public static function changesDataProvider() $pathToFixtures . '/new-method/source-code-before', $pathToFixtures . '/new-method/source-code-after', [ - 'Class (PATCH)', + 'Class (MINOR)', 'Test\Vcs\TestClass::testMethod | [public] Method has been added. | V015' ], - 'Patch change is detected.' + 'Minor change is detected.' ], 'api-class-removed-class' => [ $pathToFixtures . '/removed-class/source-code-before', diff --git a/dev/tests/Unit/bootstrap.php b/dev/tests/Unit/bootstrap.php index 0481b696..e5c95dd5 100644 --- a/dev/tests/Unit/bootstrap.php +++ b/dev/tests/Unit/bootstrap.php @@ -37,7 +37,6 @@ function ($errNo, $errStr, $errFile, $errLine) { E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', - E_STRICT => 'Strict', E_RECOVERABLE_ERROR => 'Recoverable Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', diff --git a/src/Analyzer/SystemXml/Analyzer.php b/src/Analyzer/SystemXml/Analyzer.php index 5fe050a3..d1b2206c 100644 --- a/src/Analyzer/SystemXml/Analyzer.php +++ b/src/Analyzer/SystemXml/Analyzer.php @@ -25,6 +25,8 @@ use Magento\SemanticVersionChecker\Registry\XmlRegistry; use PHPSemVerChecker\Registry\Registry; use PHPSemVerChecker\Report\Report; +use Magento\SemanticVersionChecker\Operation\SystemXml\DuplicateFieldAdded; +use RecursiveDirectoryIterator; /** * Analyzes system.xml files: @@ -92,14 +94,152 @@ public function analyze($registryBefore, $registryAfter) $beforeFile = $registryBefore->mapping[XmlRegistry::NODES_KEY][$moduleName]; $this->reportRemovedNodes($beforeFile, $removedNodes); } + if ($addedNodes) { $afterFile = $registryAfter->mapping[XmlRegistry::NODES_KEY][$moduleName]; - $this->reportAddedNodes($afterFile, $addedNodes); + if (strpos($afterFile, '_files') !== false) { + $this->reportAddedNodes($afterFile, $addedNodes); + } else { + $baseDir = $this->getBaseDir($afterFile); + foreach ($addedNodes as $nodeId => $node) { + $newNodeData = $this->getNodeData($node); + $nodePath = $newNodeData['path']; + + // Extract section, group, and fieldId with error handling + $extractedData = $this->extractSectionGroupField($nodePath); + if ($extractedData === null) { + // Skip the node if its path is invalid + continue; + } + + // Extract section, group, and fieldId + list($sectionId, $groupId, $fieldId) = $extractedData; + + // Call function to check if this field is duplicated in other system.xml files + $isDuplicated = $this->isDuplicatedFieldInXml( + $baseDir, + $sectionId, + $groupId, + $fieldId, + $afterFile + ); + + foreach ($isDuplicated as $isDuplicatedItem) { + if ($isDuplicatedItem['status'] === 'duplicate') { + $this->reportDuplicateNodes($afterFile, [$nodeId => $node]); + } else { + $this->reportAddedNodes($afterFile, [$nodeId => $node]); + } + } + } + } } } return $this->report; } + /** + * Get Magento Base directory from the path + * + * @param string $filePath + * @return string|null + */ + private function getBaseDir(string $filePath): ?string + { + $currentDir = dirname($filePath); + while ($currentDir !== '/' && $currentDir !== false) { + // Check if current directory contains files unique to Magento root + if (file_exists($currentDir . '/SECURITY.md')) { + return $currentDir; // Found the Magento base directory + } + $currentDir = dirname($currentDir); + } + return null; + } + + /** + * Search for system.xml files in both app/code and vendor directories, excluding the provided file. + * + * @param string $magentoBaseDir The base directory of Magento. + * @param string|null $excludeFile The file to exclude from the search. + * @return array An array of paths to system.xml files, excluding the specified file. + */ + private function getSystemXmlFiles(string $magentoBaseDir, ?string $excludeFile = null): array + { + $systemXmlFiles = []; + $directoryToSearch = [ + $magentoBaseDir . '/app/code' + ]; + + // Check if 'vendor' directory exists, and only add it if it does + if (is_dir($magentoBaseDir . '/vendor')) { + $directoriesToSearch[] = $magentoBaseDir . '/vendor'; + } + foreach ($directoryToSearch as $directory) { + $iterator = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)); + foreach ($iterator as $file) { + if ($file->getfileName() === 'system.xml') { + $filePath = $file->getRealPath(); + if ($filePath !== $excludeFile) { + $systemXmlFiles[] = $file->getRealPath(); + } + } + } + } + return $systemXmlFiles; + } + + /** + * Method to extract section, group and field from the Node + * + * @param string $nodePath + * @return array|null + */ + private function extractSectionGroupField(string $nodePath): ?array + { + $parts = explode('/', $nodePath); + + if (count($parts) < 3) { + // Invalid path if there are fewer than 3 parts + return null; + } + + $sectionId = $parts[0]; + $groupId = $parts[1]; + $fieldId = $parts[2]; + + return [$sectionId, $groupId, $fieldId]; + } + + /** + * Method to get Node Data using reflection class + * + * @param object|string $node + * @return array + * @throws \ReflectionException + */ + private function getNodeData(object|string $node): array + { + $data = []; + + // Use reflection to get accessible properties + $reflection = new \ReflectionClass($node); + foreach ($reflection->getMethods() as $method) { + // Skip 'getId' and 'getParent' methods for comparison + if ($method->getName() === 'getId' || $method->getName() === 'getParent') { + continue; + } + + // Dynamically call the getter methods + if (strpos($method->getName(), 'get') === 0) { + $propertyName = lcfirst(str_replace('get', '', $method->getName())); + $data[$propertyName] = $method->invoke($node); + } + } + + return $data; + } + /** * Extracts the node from $registry as an associative array. * @@ -164,13 +304,32 @@ private function reportAddedNodes(string $file, array $nodes) } } + /** + * Creates reports for $nodes considering that they have been duplicated. + * + * @param string $file + * @param NodeInterface[] $nodes + * @return void + */ + private function reportDuplicateNodes(string $file, array $nodes): void + { + foreach ($nodes as $node) { + switch (true) { + case $node instanceof Field: + $this->report->add('system', new DuplicateFieldAdded($file, $node->getPath())); + break; + } + } + } + /** * Creates reports for $modules considering that system.xml has been removed from them. * * @param array $modules * @param XmlRegistry $registryBefore + * @return void */ - private function reportRemovedFiles(array $modules, XmlRegistry $registryBefore) + private function reportRemovedFiles(array $modules, XmlRegistry $registryBefore): void { foreach ($modules as $module) { $beforeFile = $registryBefore->mapping[XmlRegistry::NODES_KEY][$module]; @@ -183,8 +342,9 @@ private function reportRemovedFiles(array $modules, XmlRegistry $registryBefore) * * @param string $file * @param NodeInterface[] $nodes + * @return void */ - private function reportRemovedNodes(string $file, array $nodes) + private function reportRemovedNodes(string $file, array $nodes): void { foreach ($nodes as $node) { switch (true) { @@ -202,4 +362,56 @@ private function reportRemovedNodes(string $file, array $nodes) } } } + + /** + * @param string|null $baseDir + * @param string $sectionId + * @param string $groupId + * @param string|null $fieldId + * @param string $afterFile + * @return array + */ + private function isDuplicatedFieldInXml( + ?string $baseDir, + string $sectionId, + string $groupId, + ?string $fieldId, + string $afterFile + ): array { + $hasDuplicate = false; + + $result = [ + 'status' => 'minor', + 'field' => $fieldId + ]; + + if ($baseDir) { + $systemXmlFiles = $this->getSystemXmlFiles($baseDir, $afterFile); + + foreach ($systemXmlFiles as $systemXmlFile) { + $xmlContent = file_get_contents($systemXmlFile); + try { + $xml = new \SimpleXMLElement($xmlContent); + } catch (\Exception $e) { + continue; // Skip this file if there's a parsing error + } + // Find nodes with the given field ID + // XPath to search for within a specific section and group + $fields = $xml->xpath("//section[@id='$sectionId']/group[@id='$groupId']/field[@id='$fieldId']"); + if (!empty($fields)) { + $hasDuplicate = true; // Set the duplicate flag to true if a match is found + break; // Since we found a duplicate, we don't need to check further for this field + } + } + if ($hasDuplicate) { + return [ + [ + 'status' => 'duplicate', + 'field' => $fieldId + ] + ]; + } + } + return [$result]; + } } diff --git a/src/Operation/SystemXml/DuplicateFieldAdded.php b/src/Operation/SystemXml/DuplicateFieldAdded.php new file mode 100644 index 00000000..8424574e --- /dev/null +++ b/src/Operation/SystemXml/DuplicateFieldAdded.php @@ -0,0 +1,34 @@ +field node is added. + */ +class DuplicateFieldAdded extends AbstractOperation +{ + /** + * @var string + */ + protected $code = 'M302'; + + /** + * @var int + */ + protected $level = Level::PATCH; + + /** + * @var string + */ + protected $reason = 'A field-node was duplicated'; +} diff --git a/src/ReportBuilder.php b/src/ReportBuilder.php index d9e391af..e39d8682 100644 --- a/src/ReportBuilder.php +++ b/src/ReportBuilder.php @@ -126,7 +126,7 @@ protected function makeVersionReport() // Customize severity level of some @api changes LevelMapping::setOverrides( [ - 'V015' => Level::PATCH, // Add public method + 'V015' => Level::MINOR, // Add public method 'V016' => Level::PATCH, // Add protected method 'V019' => Level::MINOR, // Add public property 'V020' => Level::MINOR, // Add protected property