diff --git a/composer.lock b/composer.lock index 71a0d547a..7d52a3c65 100644 --- a/composer.lock +++ b/composer.lock @@ -773,16 +773,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.31", + "version": "2.0.32", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4" + "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4", - "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f5c4c19880d45d0be3e7d24ae8ac434844a898cd", + "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd", "shasum": "" }, "require": { @@ -862,7 +862,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.31" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.32" }, "funding": [ { @@ -878,7 +878,7 @@ "type": "tidelift" } ], - "time": "2021-04-06T13:56:45+00:00" + "time": "2021-06-12T12:12:59+00:00" }, { "name": "psr/log", @@ -1649,16 +1649,16 @@ }, { "name": "symfony/options-resolver", - "version": "v5.2.4", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce" + "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", - "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/162e886ca035869866d233a2bfef70cc28f9bbe5", + "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5", "shasum": "" }, "require": { @@ -1698,7 +1698,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.2.4" + "source": "https://github.com/symfony/options-resolver/tree/v5.3.0" }, "funding": [ { @@ -1714,20 +1714,20 @@ "type": "tidelift" } ], - "time": "2021-01-27T12:56:27+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { @@ -1739,7 +1739,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1777,7 +1777,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" }, "funding": [ { @@ -1793,20 +1793,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", - "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", "shasum": "" }, "require": { @@ -1815,7 +1815,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1856,7 +1856,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" }, "funding": [ { @@ -1872,20 +1872,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0", + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0", "shasum": "" }, "require": { @@ -1894,7 +1894,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1939,7 +1939,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0" }, "funding": [ { @@ -1955,20 +1955,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/process", - "version": "v5.2.7", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e" + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e", - "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e", + "url": "https://api.github.com/repos/symfony/process/zipball/53e36cb1c160505cdaf1ef201501669c4c317191", + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191", "shasum": "" }, "require": { @@ -2001,7 +2001,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.3.0-BETA1" + "source": "https://github.com/symfony/process/tree/v5.3.0" }, "funding": [ { @@ -2017,7 +2017,7 @@ "type": "tidelift" } ], - "time": "2021-04-08T10:27:02+00:00" + "time": "2021-05-26T12:52:38+00:00" }, { "name": "twbs/bootstrap", @@ -3996,16 +3996,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.4", + "version": "9.5.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c73c6737305e779771147af66c96ca6a7ed8a741" + "reference": "89ff45ea9d70e35522fb6654a2ebc221158de276" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c73c6737305e779771147af66c96ca6a7ed8a741", - "reference": "c73c6737305e779771147af66c96ca6a7ed8a741", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/89ff45ea9d70e35522fb6654a2ebc221158de276", + "reference": "89ff45ea9d70e35522fb6654a2ebc221158de276", "shasum": "" }, "require": { @@ -4035,7 +4035,7 @@ "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^2.3", + "sebastian/type": "^2.3.2", "sebastian/version": "^3.0.2" }, "require-dev": { @@ -4083,7 +4083,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.4" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.5" }, "funding": [ { @@ -4095,7 +4095,7 @@ "type": "github" } ], - "time": "2021-03-23T07:16:29+00:00" + "time": "2021-06-05T04:49:07+00:00" }, { "name": "psr/container", @@ -4798,16 +4798,16 @@ }, { "name": "sebastian/global-state", - "version": "5.0.2", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455", - "reference": "a90ccbddffa067b51f574dea6eb25d5680839455", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { @@ -4850,7 +4850,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" }, "funding": [ { @@ -4858,7 +4858,7 @@ "type": "github" } ], - "time": "2020-10-26T15:55:19+00:00" + "time": "2021-06-11T13:31:12+00:00" }, { "name": "sebastian/lines-of-code", @@ -5149,16 +5149,16 @@ }, { "name": "sebastian/type", - "version": "2.3.1", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2" + "reference": "0d1c587401514d17e8f9258a27e23527cb1b06c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2", - "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0d1c587401514d17e8f9258a27e23527cb1b06c1", + "reference": "0d1c587401514d17e8f9258a27e23527cb1b06c1", "shasum": "" }, "require": { @@ -5193,7 +5193,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/2.3.1" + "source": "https://github.com/sebastianbergmann/type/tree/2.3.2" }, "funding": [ { @@ -5201,7 +5201,7 @@ "type": "github" } ], - "time": "2020-10-26T13:18:59+00:00" + "time": "2021-06-04T13:02:07+00:00" }, { "name": "sebastian/version", @@ -5258,16 +5258,16 @@ }, { "name": "symfony/browser-kit", - "version": "v4.4.24", + "version": "v4.4.25", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "7e6a92d5d97d826830924bd8cd22daa452c9e467" + "reference": "729b1f0eca3ef18ea4e1a29b166145aff75d8fa1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/7e6a92d5d97d826830924bd8cd22daa452c9e467", - "reference": "7e6a92d5d97d826830924bd8cd22daa452c9e467", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/729b1f0eca3ef18ea4e1a29b166145aff75d8fa1", + "reference": "729b1f0eca3ef18ea4e1a29b166145aff75d8fa1", "shasum": "" }, "require": { @@ -5309,7 +5309,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v4.4.24" + "source": "https://github.com/symfony/browser-kit/tree/v4.4.25" }, "funding": [ { @@ -5325,26 +5325,27 @@ "type": "tidelift" } ], - "time": "2021-05-16T09:52:47+00:00" + "time": "2021-05-26T17:39:37+00:00" }, { "name": "symfony/config", - "version": "v4.4.23", + "version": "v4.4.25", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "be9e601f17fc684ddfd6c675fdfcd04bb51fa928" + "reference": "2803882bb10353d277d4539635dd688a053d571c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/be9e601f17fc684ddfd6c675fdfcd04bb51fa928", - "reference": "be9e601f17fc684ddfd6c675fdfcd04bb51fa928", + "url": "https://api.github.com/repos/symfony/config/zipball/2803882bb10353d277d4539635dd688a053d571c", + "reference": "2803882bb10353d277d4539635dd688a053d571c", "shasum": "" }, "require": { "php": ">=7.1.3", "symfony/filesystem": "^3.4|^4.0|^5.0", - "symfony/polyfill-ctype": "~1.8" + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php81": "^1.22" }, "conflict": { "symfony/finder": "<3.4" @@ -5385,7 +5386,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v4.4.23" + "source": "https://github.com/symfony/config/tree/v4.4.25" }, "funding": [ { @@ -5401,24 +5402,25 @@ "type": "tidelift" } ], - "time": "2021-05-07T13:37:51+00:00" + "time": "2021-05-26T11:20:16+00:00" }, { "name": "symfony/console", - "version": "v5.2.8", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "864568fdc0208b3eba3638b6000b69d2386e6768" + "reference": "058553870f7809087fa80fa734704a21b9bcaeb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/864568fdc0208b3eba3638b6000b69d2386e6768", - "reference": "864568fdc0208b3eba3638b6000b69d2386e6768", + "url": "https://api.github.com/repos/symfony/console/zipball/058553870f7809087fa80fa734704a21b9bcaeb2", + "reference": "058553870f7809087fa80fa734704a21b9bcaeb2", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", "symfony/polyfill-php80": "^1.15", @@ -5482,7 +5484,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.2.8" + "source": "https://github.com/symfony/console/tree/v5.3.0" }, "funding": [ { @@ -5498,20 +5500,20 @@ "type": "tidelift" } ], - "time": "2021-05-11T15:45:21+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "symfony/css-selector", - "version": "v5.2.9", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "5d5f97809015102116208b976eb2edb44b689560" + "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/5d5f97809015102116208b976eb2edb44b689560", - "reference": "5d5f97809015102116208b976eb2edb44b689560", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", + "reference": "fcd0b29a7a0b1bb5bfbedc6231583d77fea04814", "shasum": "" }, "require": { @@ -5547,7 +5549,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.2.9" + "source": "https://github.com/symfony/css-selector/tree/v5.3.0" }, "funding": [ { @@ -5563,20 +5565,20 @@ "type": "tidelift" } ], - "time": "2021-05-16T13:07:46+00:00" + "time": "2021-05-26T17:40:38+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.24", + "version": "v4.4.25", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "8422396fb0b477ecbbe130907f90a0809b49c835" + "reference": "2ed2a0a6c960bf4e2e862ec77b2f2c558b83c64d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8422396fb0b477ecbbe130907f90a0809b49c835", - "reference": "8422396fb0b477ecbbe130907f90a0809b49c835", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2ed2a0a6c960bf4e2e862ec77b2f2c558b83c64d", + "reference": "2ed2a0a6c960bf4e2e862ec77b2f2c558b83c64d", "shasum": "" }, "require": { @@ -5632,7 +5634,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v4.4.24" + "source": "https://github.com/symfony/dependency-injection/tree/v4.4.25" }, "funding": [ { @@ -5648,20 +5650,20 @@ "type": "tidelift" } ], - "time": "2021-05-16T09:52:47+00:00" + "time": "2021-05-26T17:54:16+00:00" }, { "name": "symfony/dom-crawler", - "version": "v4.4.24", + "version": "v4.4.25", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "fc0bd1f215b0cd9f4efdc63bb66808f3417331bc" + "reference": "41d15bb6d6b95d2be763c514bb2494215d9c5eef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/fc0bd1f215b0cd9f4efdc63bb66808f3417331bc", - "reference": "fc0bd1f215b0cd9f4efdc63bb66808f3417331bc", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/41d15bb6d6b95d2be763c514bb2494215d9c5eef", + "reference": "41d15bb6d6b95d2be763c514bb2494215d9c5eef", "shasum": "" }, "require": { @@ -5705,7 +5707,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v4.4.24" + "source": "https://github.com/symfony/dom-crawler/tree/v4.4.25" }, "funding": [ { @@ -5721,20 +5723,20 @@ "type": "tidelift" } ], - "time": "2021-05-16T09:52:47+00:00" + "time": "2021-05-26T11:20:16+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.2.4", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d08d6ec121a425897951900ab692b612a61d6240" + "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d08d6ec121a425897951900ab692b612a61d6240", - "reference": "d08d6ec121a425897951900ab692b612a61d6240", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/67a5f354afa8e2f231081b3fa11a5912f933c3ce", + "reference": "67a5f354afa8e2f231081b3fa11a5912f933c3ce", "shasum": "" }, "require": { @@ -5790,7 +5792,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.2.4" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.3.0" }, "funding": [ { @@ -5806,7 +5808,7 @@ "type": "tidelift" } ], - "time": "2021-02-18T17:12:37+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5889,16 +5891,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.2.7", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0" + "reference": "348116319d7fb7d1faa781d26a48922428013eb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/056e92acc21d977c37e6ea8e97374b2a6c8551b0", - "reference": "056e92acc21d977c37e6ea8e97374b2a6c8551b0", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/348116319d7fb7d1faa781d26a48922428013eb2", + "reference": "348116319d7fb7d1faa781d26a48922428013eb2", "shasum": "" }, "require": { @@ -5931,7 +5933,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.2.7" + "source": "https://github.com/symfony/filesystem/tree/v5.3.0" }, "funding": [ { @@ -5947,20 +5949,20 @@ "type": "tidelift" } ], - "time": "2021-04-01T10:42:13+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", - "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/24b72c6baa32c746a4d0840147c9715e42bb68ab", + "reference": "24b72c6baa32c746a4d0840147c9715e42bb68ab", "shasum": "" }, "require": { @@ -5972,7 +5974,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6012,7 +6014,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.23.0" }, "funding": [ { @@ -6028,20 +6030,20 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" + "time": "2021-05-27T09:17:38+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", - "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65", + "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65", "shasum": "" }, "require": { @@ -6055,7 +6057,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6099,7 +6101,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.23.0" }, "funding": [ { @@ -6115,20 +6117,20 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" + "time": "2021-05-27T09:27:20+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", - "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "shasum": "" }, "require": { @@ -6140,7 +6142,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6183,7 +6185,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0" }, "funding": [ { @@ -6199,20 +6201,20 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" + "time": "2021-02-19T12:13:01+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", - "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1", + "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1", "shasum": "" }, "require": { @@ -6224,7 +6226,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6263,7 +6265,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.0" }, "funding": [ { @@ -6279,20 +6281,20 @@ "type": "tidelift" } ], - "time": "2021-01-22T09:19:47+00:00" + "time": "2021-05-27T09:27:20+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.22.1", + "version": "v1.23.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", - "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", "shasum": "" }, "require": { @@ -6301,7 +6303,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6339,7 +6341,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0" }, "funding": [ { @@ -6355,7 +6357,86 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-05-27T09:17:38+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "e66119f3de95efc359483f810c4c3e6436279436" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436", + "reference": "e66119f3de95efc359483f810c4c3e6436279436", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-21T13:25:03+00:00" }, { "name": "symfony/service-contracts", @@ -6438,16 +6519,16 @@ }, { "name": "symfony/string", - "version": "v5.2.8", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db" + "reference": "a9a0f8b6aafc5d2d1c116dcccd1573a95153515b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", - "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db", + "url": "https://api.github.com/repos/symfony/string/zipball/a9a0f8b6aafc5d2d1c116dcccd1573a95153515b", + "reference": "a9a0f8b6aafc5d2d1c116dcccd1573a95153515b", "shasum": "" }, "require": { @@ -6501,7 +6582,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.2.8" + "source": "https://github.com/symfony/string/tree/v5.3.0" }, "funding": [ { @@ -6517,20 +6598,20 @@ "type": "tidelift" } ], - "time": "2021-05-10T14:56:10+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "symfony/translation", - "version": "v4.4.24", + "version": "v4.4.25", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "424d29dfcc15575af05196de0100d7b52f650602" + "reference": "dfe132c5c6d89f90ce7f961742cc532e9ca16dd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/424d29dfcc15575af05196de0100d7b52f650602", - "reference": "424d29dfcc15575af05196de0100d7b52f650602", + "url": "https://api.github.com/repos/symfony/translation/zipball/dfe132c5c6d89f90ce7f961742cc532e9ca16dd4", + "reference": "dfe132c5c6d89f90ce7f961742cc532e9ca16dd4", "shasum": "" }, "require": { @@ -6589,7 +6670,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v4.4.24" + "source": "https://github.com/symfony/translation/tree/v4.4.25" }, "funding": [ { @@ -6605,7 +6686,7 @@ "type": "tidelift" } ], - "time": "2021-05-16T09:52:47+00:00" + "time": "2021-05-26T17:39:37+00:00" }, { "name": "symfony/translation-contracts", @@ -6687,16 +6768,16 @@ }, { "name": "symfony/yaml", - "version": "v5.2.9", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "d23115e4a3d50520abddccdbec9514baab1084c8" + "reference": "3bbcf262fceb3d8f48175302e6ba0ac96e3a5a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d23115e4a3d50520abddccdbec9514baab1084c8", - "reference": "d23115e4a3d50520abddccdbec9514baab1084c8", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3bbcf262fceb3d8f48175302e6ba0ac96e3a5a11", + "reference": "3bbcf262fceb3d8f48175302e6ba0ac96e3a5a11", "shasum": "" }, "require": { @@ -6742,7 +6823,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.2.9" + "source": "https://github.com/symfony/yaml/tree/v5.3.0" }, "funding": [ { @@ -6758,7 +6839,7 @@ "type": "tidelift" } ], - "time": "2021-05-16T13:07:46+00:00" + "time": "2021-05-26T17:43:10+00:00" }, { "name": "theseer/tokenizer", diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index a9e7e1d0e..8e05de8db 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -25,12 +25,12 @@ class InstalledVersions private static $installed = array ( 'root' => array ( - 'pretty_version' => 'dev-dev', - 'version' => 'dev-dev', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', 'aliases' => array ( ), - 'reference' => '7f4e6c2b64be07ce7587dc906e1d65494121a534', + 'reference' => '441261e2d8ac174792bcb1f37f44e74d1d5c0213', 'name' => 'zot/zap', ), 'versions' => @@ -154,12 +154,12 @@ class InstalledVersions ), 'phpseclib/phpseclib' => array ( - 'pretty_version' => '2.0.31', - 'version' => '2.0.31.0', + 'pretty_version' => '2.0.32', + 'version' => '2.0.32.0', 'aliases' => array ( ), - 'reference' => '233a920cb38636a43b18d428f9a8db1f0a1a08f4', + 'reference' => 'f5c4c19880d45d0be3e7d24ae8ac434844a898cd', ), 'psr/log' => array ( @@ -269,48 +269,48 @@ class InstalledVersions ), 'symfony/options-resolver' => array ( - 'pretty_version' => 'v5.2.4', - 'version' => '5.2.4.0', + 'pretty_version' => 'v5.3.0', + 'version' => '5.3.0.0', 'aliases' => array ( ), - 'reference' => '5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce', + 'reference' => '162e886ca035869866d233a2bfef70cc28f9bbe5', ), 'symfony/polyfill-ctype' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'c6c942b1ac76c82448322025e084cadc56048b4e', + 'reference' => '46cd95797e9df938fdd2b03693b5fca5e64b01ce', ), 'symfony/polyfill-php73' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'a678b42e92f86eca04b7fa4c0f6f19d097fb69e2', + 'reference' => 'fba8933c384d6476ab14fb7b8526e5287ca7e010', ), 'symfony/polyfill-php80' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'dc3063ba22c2a1fd2f45ed856374d79114998f91', + 'reference' => 'eca0bf41ed421bed1b57c4958bab16aa86b757d0', ), 'symfony/process' => array ( - 'pretty_version' => 'v5.2.7', - 'version' => '5.2.7.0', + 'pretty_version' => 'v5.3.0', + 'version' => '5.3.0.0', 'aliases' => array ( ), - 'reference' => '98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e', + 'reference' => '53e36cb1c160505cdaf1ef201501669c4c317191', ), 'twbs/bootstrap' => array ( @@ -348,12 +348,12 @@ class InstalledVersions ), 'zot/zap' => array ( - 'pretty_version' => 'dev-dev', - 'version' => 'dev-dev', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', 'aliases' => array ( ), - 'reference' => '7f4e6c2b64be07ce7587dc906e1d65494121a534', + 'reference' => '441261e2d8ac174792bcb1f37f44e74d1d5c0213', ), ), ); diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 1995a1f0a..8d1861046 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -435,9 +435,6 @@ return array( 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php', 'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php', 'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php', 'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 4b6bd8db8..300ae54de 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -13,12 +13,12 @@ return array( 'a1cce3d26cc15c00fcd0b3354bd72c88' => $vendorDir . '/sabre/event/lib/Promise/functions.php', '3569eecfeed3bcf0bad3c998a494ecb8' => $vendorDir . '/sabre/xml/lib/Deserializer/functions.php', '93aa591bc4ca510c520999e34229ee79' => $vendorDir . '/sabre/xml/lib/Serializer/functions.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', 'ebdb698ed4152ae445614b69b5e4bb6a' => $vendorDir . '/sabre/http/lib/functions.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', 'd08bec471a180204a14ef3c91e951f99' => $vendorDir . '/p3k/emoji-detector/src/Emoji.php', - 'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php', 'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php', + 'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php', ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index e0df5000d..fb5686d27 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -14,14 +14,14 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d 'a1cce3d26cc15c00fcd0b3354bd72c88' => __DIR__ . '/..' . '/sabre/event/lib/Promise/functions.php', '3569eecfeed3bcf0bad3c998a494ecb8' => __DIR__ . '/..' . '/sabre/xml/lib/Deserializer/functions.php', '93aa591bc4ca510c520999e34229ee79' => __DIR__ . '/..' . '/sabre/xml/lib/Serializer/functions.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', 'ebdb698ed4152ae445614b69b5e4bb6a' => __DIR__ . '/..' . '/sabre/http/lib/functions.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php', 'd08bec471a180204a14ef3c91e951f99' => __DIR__ . '/..' . '/p3k/emoji-detector/src/Emoji.php', - 'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php', 'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php', + 'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php', ); public static $prefixLengthsPsr4 = array ( @@ -652,9 +652,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', - 'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php', - 'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php', - 'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php', 'Ramsey\\Uuid\\BinaryUtils' => __DIR__ . '/..' . '/ramsey/uuid/src/BinaryUtils.php', 'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php', 'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 98cb4795c..78924c105 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -753,17 +753,17 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.31", - "version_normalized": "2.0.31.0", + "version": "2.0.32", + "version_normalized": "2.0.32.0", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4" + "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/233a920cb38636a43b18d428f9a8db1f0a1a08f4", - "reference": "233a920cb38636a43b18d428f9a8db1f0a1a08f4", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f5c4c19880d45d0be3e7d24ae8ac434844a898cd", + "reference": "f5c4c19880d45d0be3e7d24ae8ac434844a898cd", "shasum": "" }, "require": { @@ -780,7 +780,7 @@ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." }, - "time": "2021-04-06T13:56:45+00:00", + "time": "2021-06-12T12:12:59+00:00", "type": "library", "installation-source": "source", "autoload": { @@ -845,7 +845,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.31" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.32" }, "funding": [ { @@ -1619,17 +1619,17 @@ }, { "name": "symfony/options-resolver", - "version": "v5.2.4", - "version_normalized": "5.2.4.0", + "version": "v5.3.0", + "version_normalized": "5.3.0.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce" + "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", - "reference": "5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/162e886ca035869866d233a2bfef70cc28f9bbe5", + "reference": "162e886ca035869866d233a2bfef70cc28f9bbe5", "shasum": "" }, "require": { @@ -1638,9 +1638,9 @@ "symfony/polyfill-php73": "~1.0", "symfony/polyfill-php80": "^1.15" }, - "time": "2021-01-27T12:56:27+00:00", + "time": "2021-05-26T17:43:10+00:00", "type": "library", - "installation-source": "dist", + "installation-source": "source", "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" @@ -1670,6 +1670,9 @@ "configuration", "options" ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v5.3.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1688,17 +1691,17 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.22.1", - "version_normalized": "1.22.1.0", + "version": "v1.23.0", + "version_normalized": "1.23.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", - "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", "shasum": "" }, "require": { @@ -1707,18 +1710,18 @@ "suggest": { "ext-ctype": "For best performance" }, - "time": "2021-01-07T16:49:33+00:00", + "time": "2021-02-19T12:13:01+00:00", "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, - "installation-source": "dist", + "installation-source": "source", "autoload": { "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" @@ -1749,6 +1752,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1767,34 +1773,34 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.22.1", - "version_normalized": "1.22.1.0", + "version": "v1.23.0", + "version_normalized": "1.23.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", - "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", "shasum": "" }, "require": { "php": ">=7.1" }, - "time": "2021-01-07T16:49:33+00:00", + "time": "2021-02-19T12:13:01+00:00", "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, - "installation-source": "dist", + "installation-source": "source", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php73\\": "" @@ -1828,6 +1834,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1846,34 +1855,34 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.22.1", - "version_normalized": "1.22.1.0", + "version": "v1.23.0", + "version_normalized": "1.23.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", - "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0", + "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0", "shasum": "" }, "require": { "php": ">=7.1" }, - "time": "2021-01-07T16:49:33+00:00", + "time": "2021-02-19T12:13:01+00:00", "type": "library", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, - "installation-source": "dist", + "installation-source": "source", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php80\\": "" @@ -1911,6 +1920,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1929,24 +1941,24 @@ }, { "name": "symfony/process", - "version": "v5.2.7", - "version_normalized": "5.2.7.0", + "version": "v5.3.0", + "version_normalized": "5.3.0.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e" + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e", - "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e", + "url": "https://api.github.com/repos/symfony/process/zipball/53e36cb1c160505cdaf1ef201501669c4c317191", + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191", "shasum": "" }, "require": { "php": ">=7.2.5", "symfony/polyfill-php80": "^1.15" }, - "time": "2021-04-08T10:27:02+00:00", + "time": "2021-05-26T12:52:38+00:00", "type": "library", "installation-source": "source", "autoload": { @@ -1974,7 +1986,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.3.0-BETA1" + "source": "https://github.com/symfony/process/tree/v5.3.0" }, "funding": [ { diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 2e5c08c6c..eace33368 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -1,12 +1,12 @@ array ( - 'pretty_version' => 'dev-dev', - 'version' => 'dev-dev', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', 'aliases' => array ( ), - 'reference' => '7f4e6c2b64be07ce7587dc906e1d65494121a534', + 'reference' => '441261e2d8ac174792bcb1f37f44e74d1d5c0213', 'name' => 'zot/zap', ), 'versions' => @@ -130,12 +130,12 @@ ), 'phpseclib/phpseclib' => array ( - 'pretty_version' => '2.0.31', - 'version' => '2.0.31.0', + 'pretty_version' => '2.0.32', + 'version' => '2.0.32.0', 'aliases' => array ( ), - 'reference' => '233a920cb38636a43b18d428f9a8db1f0a1a08f4', + 'reference' => 'f5c4c19880d45d0be3e7d24ae8ac434844a898cd', ), 'psr/log' => array ( @@ -245,48 +245,48 @@ ), 'symfony/options-resolver' => array ( - 'pretty_version' => 'v5.2.4', - 'version' => '5.2.4.0', + 'pretty_version' => 'v5.3.0', + 'version' => '5.3.0.0', 'aliases' => array ( ), - 'reference' => '5d0f633f9bbfcf7ec642a2b5037268e61b0a62ce', + 'reference' => '162e886ca035869866d233a2bfef70cc28f9bbe5', ), 'symfony/polyfill-ctype' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'c6c942b1ac76c82448322025e084cadc56048b4e', + 'reference' => '46cd95797e9df938fdd2b03693b5fca5e64b01ce', ), 'symfony/polyfill-php73' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'a678b42e92f86eca04b7fa4c0f6f19d097fb69e2', + 'reference' => 'fba8933c384d6476ab14fb7b8526e5287ca7e010', ), 'symfony/polyfill-php80' => array ( - 'pretty_version' => 'v1.22.1', - 'version' => '1.22.1.0', + 'pretty_version' => 'v1.23.0', + 'version' => '1.23.0.0', 'aliases' => array ( ), - 'reference' => 'dc3063ba22c2a1fd2f45ed856374d79114998f91', + 'reference' => 'eca0bf41ed421bed1b57c4958bab16aa86b757d0', ), 'symfony/process' => array ( - 'pretty_version' => 'v5.2.7', - 'version' => '5.2.7.0', + 'pretty_version' => 'v5.3.0', + 'version' => '5.3.0.0', 'aliases' => array ( ), - 'reference' => '98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e', + 'reference' => '53e36cb1c160505cdaf1ef201501669c4c317191', ), 'twbs/bootstrap' => array ( @@ -324,12 +324,12 @@ ), 'zot/zap' => array ( - 'pretty_version' => 'dev-dev', - 'version' => 'dev-dev', + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', 'aliases' => array ( ), - 'reference' => '7f4e6c2b64be07ce7587dc906e1d65494121a534', + 'reference' => '441261e2d8ac174792bcb1f37f44e74d1d5c0213', ), ), ); diff --git a/vendor/phpseclib/phpseclib/.travis.yml b/vendor/phpseclib/phpseclib/.travis.yml index b6a5d303b..5e0d77277 100644 --- a/vendor/phpseclib/phpseclib/.travis.yml +++ b/vendor/phpseclib/phpseclib/.travis.yml @@ -28,13 +28,7 @@ matrix: before_install: true install: - - wget http://ftp.gnu.org/gnu/parallel/parallel-20170822.tar.bz2 - - tar -xvjf parallel* - - cd parallel-20170822 - - ./configure - - make - - sudo make install - - cd .. + - phpenv config-rm xdebug.ini - eval `ssh-agent -s` - travis/setup-secure-shell.sh - sh -c "if [ '$TRAVIS_PHP_VERSION' != 'hhvm' -a `php -r "echo (int) version_compare(PHP_VERSION, '7.0', '<');"` = "1" ]; then travis/install-php-extensions.sh; fi" diff --git a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php index 811d039d3..e26fe41dd 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php @@ -2580,9 +2580,9 @@ class RSA $offset+= $patternMatch ? 0 : 1; } - // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation // to protect against timing attacks - if (!$hashesMatch & !$patternMatch) { + if (!$hashesMatch | !$patternMatch) { user_error('Decryption error'); return false; } diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php index dc5b78f64..d1a7719f8 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php +++ b/vendor/phpseclib/phpseclib/phpseclib/File/ASN1.php @@ -993,7 +993,10 @@ class ASN1 case self::TYPE_GENERALIZED_TIME: $format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y'; $format.= 'mdHis'; + // if $source does _not_ include timezone information within it then assume that the timezone is GMT $date = new DateTime($source, new DateTimeZone('GMT')); + // if $source _does_ include timezone information within it then convert the time to GMT + $date->setTimezone(new DateTimeZone('GMT')); $value = $date->format($format) . 'Z'; break; case self::TYPE_BIT_STRING: diff --git a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php index 7b1b1cfad..fa3c0264e 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/File/X509.php +++ b/vendor/phpseclib/phpseclib/phpseclib/File/X509.php @@ -5058,7 +5058,7 @@ class X509 $temp = $str; } else { $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $str, 1); + $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); } // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php index 34741831b..f9bb22352 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SFTP.php @@ -269,6 +269,16 @@ class SFTP extends SSH2 */ var $preserveTime = false; + /** + * Was the last packet due to the channels being closed or not? + * + * @see self::get() + * @see self::get_sftp_packet() + * @var bool + * @access private + */ + var $channel_close = false; + /** * Default Constructor. * @@ -425,6 +435,17 @@ class SFTP extends SSH2 return false; } + return $this->_init_sftp_connection(); + } + + /** + * (Re)initializes the SFTP channel + * + * @return bool + * @access private + */ + function _init_sftp_connection() + { $this->window_size_server_to_client[self::CHANNEL] = $this->window_size; $packet = pack( @@ -2293,7 +2314,13 @@ class SFTP extends SSH2 if ($fclose_check) { fclose($fp); } - user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + // maybe the file was successfully transferred, maybe it wasn't + if ($this->channel_close) { + $this->_init_sftp_connection(); + return false; + } else { + user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS'); + } } $response = null; } @@ -3055,6 +3082,8 @@ class SFTP extends SSH2 */ function _get_sftp_packet($request_id = null) { + $this->channel_close = false; + if (isset($request_id) && isset($this->requestBuffer[$request_id])) { $this->packet_type = $this->requestBuffer[$request_id]['packet_type']; $temp = $this->requestBuffer[$request_id]['packet']; @@ -3071,7 +3100,10 @@ class SFTP extends SSH2 // SFTP packet length while (strlen($this->packet_buffer) < 4) { $temp = $this->_get_channel_packet(self::CHANNEL, true); - if (is_bool($temp)) { + if ($temp === true) { + if ($this->channel_status[self::CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) { + $this->channel_close = true; + } $this->packet_type = false; $this->packet_buffer = ''; return false; diff --git a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php index f8f8dcfde..e449d987a 100644 --- a/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php +++ b/vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php @@ -970,6 +970,14 @@ class SSH2 */ var $auth = array(); + /** + * The authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @var array|null + */ + private $auth_methods_to_continue = null; + /** * Default Constructor. * @@ -1347,6 +1355,7 @@ class SSH2 function _key_exchange($kexinit_payload_server = false) { $preferred = $this->preferred; + $send_kex = true; $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : @@ -1430,7 +1439,7 @@ class SSH2 0 ); - if ($this->send_kex_first) { + if ($kexinit_payload_server === false) { if (!$this->_send_binary_packet($kexinit_payload_client)) { return false; } @@ -1446,6 +1455,8 @@ class SSH2 user_error('Expected SSH_MSG_KEXINIT'); return false; } + + $send_kex = false; } $response = $kexinit_payload_server; @@ -1518,7 +1529,7 @@ class SSH2 extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); $first_kex_packet_follows = $first_kex_packet_follows != 0; - if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) { + if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) { return false; } @@ -2131,7 +2142,7 @@ class SSH2 // try logging with 'none' as an authentication method first since that's what // PuTTY does - if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP') { + if (substr($this->server_identifier, 0, 13) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { if ($this->_login($username)) { return true; } @@ -2275,7 +2286,9 @@ class SSH2 case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; return true; - //case NET_SSH2_MSG_USERAUTH_FAILURE: + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); default: return false; } @@ -2347,6 +2360,7 @@ class SSH2 } extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); + $this->auth_methods_to_continue = $auth_methods; if (!strlen($response)) { return false; } @@ -2519,6 +2533,8 @@ class SSH2 case NET_SSH2_MSG_USERAUTH_SUCCESS: return true; case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; } @@ -2627,8 +2643,9 @@ class SSH2 if (strlen($response) < 4) { return false; } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; return false; case NET_SSH2_MSG_USERAUTH_PK_OK: // we'll just take it on faith that the public key blob and the public key algorithm name are as @@ -2669,6 +2686,8 @@ class SSH2 switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: // either the login is bad or the server employs multi-factor authentication + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= self::MASK_LOGIN; @@ -3319,7 +3338,7 @@ class SSH2 $read = array($this->fsock); $write = $except = null; - if ($this->curTimeout <= 0) { + if (!$this->curTimeout) { if ($this->keepAlive <= 0) { @stream_select($read, $write, $except, null); } else { @@ -3513,6 +3532,10 @@ class SSH2 // only called when we've already logged in if (($this->bitmap & self::MASK_CONNECTED) && $this->isAuthenticated()) { + if (is_bool($payload)) { + return $payload; + } + switch (ord($payload[0])) { case NET_SSH2_MSG_CHANNEL_REQUEST: if (strlen($payload) == 31) { @@ -5143,4 +5166,15 @@ class SSH2 ); } } + + /** + * Return the list of authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @return array|null + */ + public function getAuthMethodsToContinue() + { + return $this->auth_methods_to_continue; + } } diff --git a/vendor/phpseclib/phpseclib/travis/run-phpunit.sh b/vendor/phpseclib/phpseclib/travis/run-phpunit.sh index 31b98afcf..97d98fdaf 100755 --- a/vendor/phpseclib/phpseclib/travis/run-phpunit.sh +++ b/vendor/phpseclib/phpseclib/travis/run-phpunit.sh @@ -37,15 +37,8 @@ then find tests -type f -name "*.php" -print0 | xargs -0 sed -i 's/extends Unit_Crypt_Hash_\(SHA512Test\|SHA256Test\)/extends \1/g' fi -if [ "$TRAVIS_PHP_VERSION" = 'hhvm' -o `php -r "echo (int) version_compare(PHP_VERSION, '7.0', '>=');"` = "1" ] -then - find tests -type f -name "*Test.php" | \ - parallel --gnu --keep-order \ - "echo '== {} =='; \"$PHPUNIT\" $PHPUNIT_ARGS {};" -else - "$PHPUNIT" \ - $PHPUNIT_ARGS \ - --coverage-text \ - --coverage-clover code_coverage/clover.xml \ - --coverage-html code_coverage/ -fi +"$PHPUNIT" \ + $PHPUNIT_ARGS \ + --coverage-text \ + --coverage-clover code_coverage/clover.xml \ + --coverage-html code_coverage/ diff --git a/vendor/symfony/options-resolver/.gitattributes b/vendor/symfony/options-resolver/.gitattributes new file mode 100644 index 000000000..84c7add05 --- /dev/null +++ b/vendor/symfony/options-resolver/.gitattributes @@ -0,0 +1,4 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/vendor/symfony/options-resolver/.gitignore b/vendor/symfony/options-resolver/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/vendor/symfony/options-resolver/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/options-resolver/CHANGELOG.md b/vendor/symfony/options-resolver/CHANGELOG.md index d996e309f..84c45946a 100644 --- a/vendor/symfony/options-resolver/CHANGELOG.md +++ b/vendor/symfony/options-resolver/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.3 +--- + + * Add prototype definition for nested options + 5.1.0 ----- diff --git a/vendor/symfony/options-resolver/OptionsResolver.php b/vendor/symfony/options-resolver/OptionsResolver.php index e369615e5..a8ae153f4 100644 --- a/vendor/symfony/options-resolver/OptionsResolver.php +++ b/vendor/symfony/options-resolver/OptionsResolver.php @@ -130,6 +130,16 @@ class OptionsResolver implements Options private $parentsOptions = []; + /** + * Whether the whole options definition is marked as array prototype. + */ + private $prototype; + + /** + * The prototype array's index that is being read. + */ + private $prototypeIndex; + /** * Sets the default value of a given option. * @@ -789,6 +799,33 @@ class OptionsResolver implements Options return $this->info[$option] ?? null; } + /** + * Marks the whole options definition as array prototype. + * + * @return $this + * + * @throws AccessException If called from a lazy option, a normalizer or a root definition + */ + public function setPrototype(bool $prototype): self + { + if ($this->locked) { + throw new AccessException('The prototype property cannot be set from a lazy option or normalizer.'); + } + + if (null === $this->prototype && $prototype) { + throw new AccessException('The prototype property cannot be set from a root definition.'); + } + + $this->prototype = $prototype; + + return $this; + } + + public function isPrototype(): bool + { + return $this->prototype ?? false; + } + /** * Removes the option with the given name. * @@ -970,13 +1007,29 @@ class OptionsResolver implements Options $this->calling[$option] = true; try { $resolver = new self(); + $resolver->prototype = false; $resolver->parentsOptions = $this->parentsOptions; $resolver->parentsOptions[] = $option; foreach ($this->nested[$option] as $closure) { $closure($resolver, $this); } - $value = $resolver->resolve($value); + + if ($resolver->prototype) { + $values = []; + foreach ($value as $index => $prototypeValue) { + if (!\is_array($prototypeValue)) { + throw new InvalidOptionsException(sprintf('The value of the option "%s" is expected to be of type array of array, but is of type array of "%s".', $this->formatOptions([$option]), get_debug_type($prototypeValue))); + } + + $resolver->prototypeIndex = $index; + $values[$index] = $resolver->resolve($prototypeValue); + } + $value = $values; + } else { + $value = $resolver->resolve($value); + } } finally { + $resolver->prototypeIndex = null; unset($this->calling[$option]); } } @@ -1286,6 +1339,10 @@ class OptionsResolver implements Options $prefix .= sprintf('[%s]', implode('][', $this->parentsOptions)); } + if ($this->prototype && null !== $this->prototypeIndex) { + $prefix .= sprintf('[%s]', $this->prototypeIndex); + } + $options = array_map(static function (string $option) use ($prefix): string { return sprintf('%s[%s]', $prefix, $option); }, $options); diff --git a/vendor/symfony/options-resolver/README.md b/vendor/symfony/options-resolver/README.md index 245e69b54..c63b9005e 100644 --- a/vendor/symfony/options-resolver/README.md +++ b/vendor/symfony/options-resolver/README.md @@ -8,8 +8,8 @@ value), normalization and more. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/options_resolver.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/options_resolver.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php new file mode 100644 index 000000000..17d38bf9a --- /dev/null +++ b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php @@ -0,0 +1,292 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\OptionsResolver\Tests\Debug; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector; +use Symfony\Component\OptionsResolver\Exception\NoConfigurationException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class OptionsResolverIntrospectorTest extends TestCase +{ + public function testGetDefault() + { + $resolver = new OptionsResolver(); + $resolver->setDefault($option = 'foo', 'bar'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getDefault($option)); + } + + public function testGetDefaultNull() + { + $resolver = new OptionsResolver(); + $resolver->setDefault($option = 'foo', null); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertNull($debug->getDefault($option)); + } + + public function testGetDefaultThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No default value was set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getDefault($option); + } + + public function testGetDefaultThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getDefault('foo'); + } + + public function testGetLazyClosures() + { + $resolver = new OptionsResolver(); + $closures = []; + $resolver->setDefault($option = 'foo', $closures[] = function (Options $options) {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($closures, $debug->getLazyClosures($option)); + } + + public function testGetLazyClosuresThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No lazy closures were set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getLazyClosures($option); + } + + public function testGetLazyClosuresThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getLazyClosures('foo'); + } + + public function testGetAllowedTypes() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setAllowedTypes($option = 'foo', $allowedTypes = ['string', 'bool']); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($allowedTypes, $debug->getAllowedTypes($option)); + } + + public function testGetAllowedTypesThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No allowed types were set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedTypes($option)); + } + + public function testGetAllowedTypesThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedTypes('foo')); + } + + public function testGetAllowedValues() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setAllowedValues($option = 'foo', $allowedValues = ['bar', 'baz']); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($allowedValues, $debug->getAllowedValues($option)); + } + + public function testGetAllowedValuesThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No allowed values were set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedValues($option)); + } + + public function testGetAllowedValuesThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedValues('foo')); + } + + public function testGetNormalizer() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setNormalizer($option = 'foo', $normalizer = function () {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($normalizer, $debug->getNormalizer($option)); + } + + public function testGetNormalizerThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No normalizer was set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getNormalizer($option)); + } + + public function testGetNormalizerThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getNormalizer('foo')); + } + + public function testGetNormalizers() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->addNormalizer('foo', $normalizer1 = function () {}); + $resolver->addNormalizer('foo', $normalizer2 = function () {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame([$normalizer1, $normalizer2], $debug->getNormalizers('foo')); + } + + public function testGetNormalizersThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No normalizer was set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getNormalizers('foo'); + } + + public function testGetNormalizersThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getNormalizers('foo'); + } + + /** + * @group legacy + */ + public function testGetDeprecationMessage() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->setDeprecated('foo', 'The option "foo" is deprecated.'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('The option "foo" is deprecated.', $debug->getDeprecationMessage('foo')); + } + + /** + * @group legacy + */ + public function testGetClosureDeprecationMessage() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->setDeprecated('foo', $closure = function (Options $options, $value) {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($closure, $debug->getDeprecationMessage('foo')); + } + + public function testGetDeprecation() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->setDeprecated('foo', 'vendor/package', '1.1', 'The option "foo" is deprecated.'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame([ + 'package' => 'vendor/package', + 'version' => '1.1', + 'message' => 'The option "foo" is deprecated.', + ], $debug->getDeprecation('foo')); + } + + public function testGetClosureDeprecation() + { + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + $resolver->setDeprecated('foo', 'vendor/package', '1.1', $closure = function (Options $options, $value) {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame([ + 'package' => 'vendor/package', + 'version' => '1.1', + 'message' => $closure, + ], $debug->getDeprecation('foo')); + } + + public function testGetDeprecationMessageThrowsOnNoConfiguredValue() + { + $this->expectException(NoConfigurationException::class); + $this->expectExceptionMessage('No deprecation was set for the "foo" option.'); + $resolver = new OptionsResolver(); + $resolver->setDefined('foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getDeprecation('foo'); + } + + public function testGetDeprecationMessageThrowsOnNotDefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist.'); + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $debug->getDeprecation('foo'); + } +} diff --git a/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php new file mode 100644 index 000000000..3c36225e1 --- /dev/null +++ b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php @@ -0,0 +1,2594 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\OptionsResolver\Tests; + +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; +use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector; +use Symfony\Component\OptionsResolver\Exception\AccessException; +use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Exception\MissingOptionsException; +use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException; +use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class OptionsResolverTest extends TestCase +{ + use ExpectDeprecationTrait; + + /** + * @var OptionsResolver + */ + private $resolver; + + protected function setUp(): void + { + $this->resolver = new OptionsResolver(); + } + + public function testResolveFailsIfNonExistingOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "foo" does not exist. Defined options are: "a", "z".'); + $this->resolver->setDefault('z', '1'); + $this->resolver->setDefault('a', '2'); + + $this->resolver->resolve(['foo' => 'bar']); + } + + public function testResolveFailsIfMultipleNonExistingOptions() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The options "baz", "foo", "ping" do not exist. Defined options are: "a", "z".'); + $this->resolver->setDefault('z', '1'); + $this->resolver->setDefault('a', '2'); + + $this->resolver->resolve(['ping' => 'pong', 'foo' => 'bar', 'baz' => 'bam']); + } + + public function testResolveFailsFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->resolve([]); + }); + + $this->resolver->resolve(); + } + + public function testSetDefaultReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefault('foo', 'bar')); + } + + public function testSetDefault() + { + $this->resolver->setDefault('one', '1'); + $this->resolver->setDefault('two', '20'); + + $this->assertEquals([ + 'one' => '1', + 'two' => '20', + ], $this->resolver->resolve()); + } + + public function testFailIfSetDefaultFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('lazy', function (Options $options) { + $options->setDefault('default', 42); + }); + + $this->resolver->resolve(); + } + + public function testHasDefault() + { + $this->assertFalse($this->resolver->hasDefault('foo')); + $this->resolver->setDefault('foo', 42); + $this->assertTrue($this->resolver->hasDefault('foo')); + } + + public function testHasDefaultWithNullValue() + { + $this->assertFalse($this->resolver->hasDefault('foo')); + $this->resolver->setDefault('foo', null); + $this->assertTrue($this->resolver->hasDefault('foo')); + } + + public function testSetLazyReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefault('foo', function (Options $options) {})); + } + + public function testSetLazyClosure() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + + $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); + } + + public function testClosureWithoutTypeHintNotInvoked() + { + $closure = function ($options) { + Assert::fail('Should not be called'); + }; + + $this->resolver->setDefault('foo', $closure); + + $this->assertSame(['foo' => $closure], $this->resolver->resolve()); + } + + public function testClosureWithoutParametersNotInvoked() + { + $closure = function () { + Assert::fail('Should not be called'); + }; + + $this->resolver->setDefault('foo', $closure); + + $this->assertSame(['foo' => $closure], $this->resolver->resolve()); + } + + public function testAccessPreviousDefaultValue() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + + // defined by subclass + $this->resolver->setDefault('foo', function (Options $options, $previousValue) { + Assert::assertEquals('bar', $previousValue); + + return 'lazy'; + }); + + $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); + } + + public function testAccessPreviousLazyDefaultValue() + { + // defined by superclass + $this->resolver->setDefault('foo', function (Options $options) { + return 'bar'; + }); + + // defined by subclass + $this->resolver->setDefault('foo', function (Options $options, $previousValue) { + Assert::assertEquals('bar', $previousValue); + + return 'lazy'; + }); + + $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); + } + + public function testPreviousValueIsNotEvaluatedIfNoSecondArgument() + { + // defined by superclass + $this->resolver->setDefault('foo', function () { + Assert::fail('Should not be called'); + }); + + // defined by subclass, no $previousValue argument defined! + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + + $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); + } + + public function testOverwrittenLazyOptionNotEvaluated() + { + $this->resolver->setDefault('foo', function (Options $options) { + Assert::fail('Should not be called'); + }); + + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testInvokeEachLazyOptionOnlyOnce() + { + $calls = 0; + + $this->resolver->setDefault('lazy1', function (Options $options) use (&$calls) { + Assert::assertSame(1, ++$calls); + + $options['lazy2']; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) use (&$calls) { + Assert::assertSame(2, ++$calls); + }); + + $this->resolver->resolve(); + + $this->assertSame(2, $calls); + } + + public function testSetRequiredReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setRequired('foo')); + } + + public function testFailIfSetRequiredFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setRequired('bar'); + }); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfRequiredOptionMissing() + { + $this->expectException(MissingOptionsException::class); + $this->resolver->setRequired('foo'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfRequiredOptionSet() + { + $this->resolver->setRequired('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfRequiredOptionPassed() + { + $this->resolver->setRequired('foo'); + + $this->assertNotEmpty($this->resolver->resolve(['foo' => 'bar'])); + } + + public function testIsRequired() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testRequiredIfSetBefore() + { + $this->assertFalse($this->resolver->isRequired('foo')); + + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setRequired('foo'); + + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testStillRequiredAfterSet() + { + $this->assertFalse($this->resolver->isRequired('foo')); + + $this->resolver->setRequired('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testIsNotRequiredAfterRemove() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testIsNotRequiredAfterClear() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testGetRequiredOptions() + { + $this->resolver->setRequired(['foo', 'bar']); + $this->resolver->setDefault('bam', 'baz'); + $this->resolver->setDefault('foo', 'boo'); + + $this->assertSame(['foo', 'bar'], $this->resolver->getRequiredOptions()); + } + + public function testIsMissingIfNotSet() + { + $this->assertFalse($this->resolver->isMissing('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingIfSet() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertFalse($this->resolver->isMissing('foo')); + $this->resolver->setRequired('foo'); + $this->assertFalse($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingAfterRemove() + { + $this->resolver->setRequired('foo'); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingAfterClear() + { + $this->resolver->setRequired('foo'); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testGetMissingOptions() + { + $this->resolver->setRequired(['foo', 'bar']); + $this->resolver->setDefault('bam', 'baz'); + $this->resolver->setDefault('foo', 'boo'); + + $this->assertSame(['bar'], $this->resolver->getMissingOptions()); + } + + public function testFailIfSetDefinedFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setDefined('bar'); + }); + + $this->resolver->resolve(); + } + + public function testDefinedOptionsNotIncludedInResolvedOptions() + { + $this->resolver->setDefined('foo'); + + $this->assertSame([], $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfDefaultSetBefore() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefined('foo'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfDefaultSetAfter() + { + $this->resolver->setDefined('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfPassedToResolve() + { + $this->resolver->setDefined('foo'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve(['foo' => 'bar'])); + } + + public function testIsDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testLazyOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefault('foo', function (Options $options) {}); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testRequiredOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testSetOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefault('foo', 'bar'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testGetDefinedOptions() + { + $this->resolver->setDefined(['foo', 'bar']); + $this->resolver->setDefault('baz', 'bam'); + $this->resolver->setRequired('boo'); + + $this->assertSame(['foo', 'bar', 'baz', 'boo'], $this->resolver->getDefinedOptions()); + } + + public function testRemovedOptionsAreNotDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isDefined('foo')); + } + + public function testClearedOptionsAreNotDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isDefined('foo')); + } + + public function testFailIfSetDeprecatedFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver + ->setDefault('bar', 'baz') + ->setDefault('foo', function (Options $options) { + $options->setDeprecated('bar'); + }) + ->resolve() + ; + } + + public function testSetDeprecatedFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->setDeprecated('foo'); + } + + public function testSetDeprecatedFailsIfInvalidDeprecationMessageType() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid type for deprecation message argument, expected string or \Closure, but got "bool".'); + $this->resolver + ->setDefined('foo') + ->setDeprecated('foo', 'vendor/package', '1.1', true) + ; + } + + public function testLazyDeprecationFailsIfInvalidDeprecationMessageType() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid type for deprecation message, expected string but got "bool", return an empty string to ignore.'); + $this->resolver + ->setDefined('foo') + ->setDeprecated('foo', 'vendor/package', '1.1', function (Options $options, $value) { + return false; + }) + ; + $this->resolver->resolve(['foo' => null]); + } + + public function testFailsIfCyclicDependencyBetweenDeprecation() + { + $this->expectException(OptionDefinitionException::class); + $this->expectExceptionMessage('The options "foo", "bar" have a cyclic dependency.'); + $this->resolver + ->setDefined(['foo', 'bar']) + ->setDeprecated('foo', 'vendor/package', '1.1', function (Options $options, $value) { + $options['bar']; + }) + ->setDeprecated('bar', 'vendor/package', '1.1', function (Options $options, $value) { + $options['foo']; + }) + ; + $this->resolver->resolve(['foo' => null, 'bar' => null]); + } + + public function testIsDeprecated() + { + $this->resolver + ->setDefined('foo') + ->setDeprecated('foo', 'vendor/package', '1.1') + ; + $this->assertTrue($this->resolver->isDeprecated('foo')); + } + + public function testIsNotDeprecatedIfEmptyString() + { + $this->resolver + ->setDefined('foo') + ->setDeprecated('foo', 'vendor/package', '1.1', '') + ; + $this->assertFalse($this->resolver->isDeprecated('foo')); + } + + /** + * @dataProvider provideDeprecationData + */ + public function testDeprecationMessages(\Closure $configureOptions, array $options, ?array $expectedError, int $expectedCount) + { + $count = 0; + error_clear_last(); + set_error_handler(function (int $type) use (&$count) { + $this->assertSame(\E_USER_DEPRECATED, $type); + + ++$count; + + return false; + }); + $e = error_reporting(0); + + $configureOptions($this->resolver); + $this->resolver->resolve($options); + + error_reporting($e); + restore_error_handler(); + + $lastError = error_get_last(); + unset($lastError['file'], $lastError['line']); + + $this->assertSame($expectedError, $lastError); + $this->assertSame($expectedCount, $count); + } + + public function provideDeprecationData() + { + yield 'It deprecates an option with default message' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefined(['foo', 'bar']) + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + ; + }, + ['foo' => 'baz'], + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: The option "foo" is deprecated.', + ], + 1, + ]; + + yield 'It deprecates an option with custom message' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefined('foo') + ->setDefault('bar', function (Options $options) { + return $options['foo']; + }) + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "foo" is deprecated, use "bar" option instead.') + ; + }, + ['foo' => 'baz'], + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: The option "foo" is deprecated, use "bar" option instead.', + ], + 2, + ]; + + yield 'It deprecates an option evaluated in another definition' => [ + function (OptionsResolver $resolver) { + // defined by superclass + $resolver + ->setDefault('foo', null) + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + ; + // defined by subclass + $resolver->setDefault('bar', function (Options $options) { + return $options['foo']; // It triggers a deprecation + }); + }, + [], + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: The option "foo" is deprecated.', + ], + 1, + ]; + + yield 'It deprecates allowed type and value' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefault('foo', null) + ->setAllowedTypes('foo', ['null', 'string', \stdClass::class]) + ->setDeprecated('foo', 'vendor/package', '1.1', function (Options $options, $value) { + if ($value instanceof \stdClass) { + return sprintf('Passing an instance of "%s" to option "foo" is deprecated, pass its FQCN instead.', \stdClass::class); + } + + return ''; + }) + ; + }, + ['foo' => new \stdClass()], + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: Passing an instance of "stdClass" to option "foo" is deprecated, pass its FQCN instead.', + ], + 1, + ]; + + yield 'It triggers a deprecation based on the value only if option is provided by the user' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefined('foo') + ->setAllowedTypes('foo', ['null', 'bool']) + ->setDeprecated('foo', 'vendor/package', '1.1', function (Options $options, $value) { + if (!\is_bool($value)) { + return 'Passing a value different than true or false is deprecated.'; + } + + return ''; + }) + ->setDefault('baz', null) + ->setAllowedTypes('baz', ['null', 'int']) + ->setDeprecated('baz', 'vendor/package', '1.1', function (Options $options, $value) { + if (!\is_int($value)) { + return 'Not passing an integer is deprecated.'; + } + + return ''; + }) + ->setDefault('bar', function (Options $options) { + $options['baz']; // It does not triggers a deprecation + + return $options['foo']; // It does not triggers a deprecation + }) + ; + }, + ['foo' => null], // It triggers a deprecation + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: Passing a value different than true or false is deprecated.', + ], + 1, + ]; + + yield 'It ignores a deprecation if closure returns an empty string' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefault('foo', null) + ->setDeprecated('foo', 'vendor/package', '1.1', function (Options $options, $value) { + return ''; + }) + ; + }, + ['foo' => Bar::class], + null, + 0, + ]; + + yield 'It deprecates value depending on other option value' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefault('widget', null) + ->setDefault('date_format', null) + ->setDeprecated('date_format', 'vendor/package', '1.1', function (Options $options, $dateFormat) { + if (null !== $dateFormat && 'single_text' === $options['widget']) { + return 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.'; + } + + return ''; + }) + ; + }, + ['widget' => 'single_text', 'date_format' => 2], + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.', + ], + 1, + ]; + + yield 'It triggers a deprecation for each evaluation' => [ + function (OptionsResolver $resolver) { + $resolver + // defined by superclass + ->setDefined('foo') + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + // defined by subclass + ->setDefault('bar', function (Options $options) { + return $options['foo']; // It triggers a deprecation + }) + ->setNormalizer('bar', function (Options $options, $value) { + $options['foo']; // It triggers a deprecation + $options['foo']; // It triggers a deprecation + + return $value; + }) + ; + }, + ['foo' => 'baz'], // It triggers a deprecation + [ + 'type' => \E_USER_DEPRECATED, + 'message' => 'Since vendor/package 1.1: The option "foo" is deprecated.', + ], + 4, + ]; + + yield 'It ignores a deprecation if no option is provided by the user' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefined('foo') + ->setDefault('bar', null) + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + ->setDeprecated('bar', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + ; + }, + [], + null, + 0, + ]; + + yield 'It explicitly ignores a deprecation' => [ + function (OptionsResolver $resolver) { + $resolver + ->setDefault('baz', function (Options $options) { + return $options->offsetGet('foo', false); + }) + ->setDefault('foo', null) + ->setDeprecated('foo', 'vendor/package', '1.1', 'The option "%name%" is deprecated.') + ->setDefault('bar', function (Options $options) { + return $options->offsetGet('foo', false); + }) + ; + }, + [], + null, + 0, + ]; + } + + public function testSetAllowedTypesFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->setAllowedTypes('foo', 'string'); + } + + public function testResolveTypedArray() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $options = $this->resolver->resolve(['foo' => ['bar', 'baz']]); + + $this->assertSame(['foo' => ['bar', 'baz']], $options); + } + + public function testFailIfSetAllowedTypesFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setAllowedTypes('bar', 'string'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfInvalidTypedArray() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + + $this->resolver->resolve(['foo' => [new \DateTime()]]); + } + + public function testResolveFailsWithNonArray() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + + $this->resolver->resolve(['foo' => 'bar']); + } + + public function testResolveFailsIfTypedArrayContainsInvalidTypes() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass|array|DateTime".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + $values = range(1, 5); + $values[] = new \stdClass(); + $values[] = []; + $values[] = new \DateTime(); + $values[] = 123; + + $this->resolver->resolve(['foo' => $values]); + } + + public function testResolveFailsWithCorrectLevelsButWrongScalar() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "float".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->resolver->resolve([ + 'foo' => [ + [1.2], + ], + ]); + } + + /** + * @dataProvider provideInvalidTypes + */ + public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage) + { + $this->resolver->setDefined('option'); + $this->resolver->setAllowedTypes('option', $allowedType); + + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage($exceptionMessage); + + $this->resolver->resolve(['option' => $actualType]); + } + + public function provideInvalidTypes() + { + return [ + [true, 'string', 'The option "option" with value true is expected to be of type "string", but is of type "bool".'], + [false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "bool".'], + [fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource (stream)".'], + [[], 'string', 'The option "option" with value array is expected to be of type "string", but is of type "array".'], + [new OptionsResolver(), 'string', 'The option "option" with value Symfony\Component\OptionsResolver\OptionsResolver is expected to be of type "string", but is of type "Symfony\Component\OptionsResolver\OptionsResolver".'], + [42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "int".'], + [null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "null".'], + ['bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'], + [['foo', 12], 'string[]', 'The option "option" with value array is expected to be of type "string[]", but one of the elements is of type "int".'], + [123, ['string[]', 'string'], 'The option "option" with value 123 is expected to be of type "string[]" or "string", but is of type "int".'], + [[null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "null".'], + [['string', null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "null".'], + [[\stdClass::class], ['string'], 'The option "option" with value array is expected to be of type "string", but is of type "array".'], + ]; + } + + public function testResolveSucceedsIfValidType() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveFailsIfInvalidTypeMultiple() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value 42 is expected to be of type "string" or "bool", but is of type "int".'); + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedTypes('foo', ['string', 'bool']); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidTypeMultiple() + { + $this->resolver->setDefault('foo', true); + $this->resolver->setAllowedTypes('foo', ['string', 'bool']); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfInstanceOfClass() + { + $this->resolver->setDefault('foo', new \stdClass()); + $this->resolver->setAllowedTypes('foo', '\stdClass'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfTypedArray() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedTypes('foo', ['null', 'DateTime[]']); + + $data = [ + 'foo' => [ + new \DateTime(), + new \DateTime(), + ], + ]; + $result = $this->resolver->resolve($data); + $this->assertEquals($data, $result); + } + + public function testResolveFailsIfNotInstanceOfClass() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', '\stdClass'); + + $this->resolver->resolve(); + } + + public function testAddAllowedTypesFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->addAllowedTypes('foo', 'string'); + } + + public function testFailIfAddAllowedTypesFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->addAllowedTypes('bar', 'string'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfInvalidAddedType() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedTypes('foo', 'string'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedType() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedTypes('foo', 'string'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveFailsIfInvalidAddedTypeMultiple() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedTypes('foo', ['string', 'bool']); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedTypeMultiple() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedTypes('foo', ['string', 'bool']); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testAddAllowedTypesDoesNotOverwrite() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + $this->resolver->addAllowedTypes('foo', 'bool'); + + $this->resolver->setDefault('foo', 'bar'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testAddAllowedTypesDoesNotOverwrite2() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + $this->resolver->addAllowedTypes('foo', 'bool'); + + $this->resolver->setDefault('foo', false); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testSetAllowedValuesFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->setAllowedValues('foo', 'bar'); + } + + public function testFailIfSetAllowedValuesFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setAllowedValues('bar', 'baz'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfInvalidValue() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value 42 is invalid. Accepted values are: "bar".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->resolver->resolve(['foo' => 42]); + } + + public function testResolveFailsIfInvalidValueIsNull() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value null is invalid. Accepted values are: "bar".'); + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfInvalidValueStrict() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', '42'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidValue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testResolveSucceedsIfValidValueIsNull() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedValues('foo', null); + + $this->assertEquals(['foo' => null], $this->resolver->resolve()); + } + + public function testResolveFailsIfInvalidValueMultiple() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value 42 is invalid. Accepted values are: "bar", false, null.'); + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', ['bar', false, null]); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidValueMultiple() + { + $this->resolver->setDefault('foo', 'baz'); + $this->resolver->setAllowedValues('foo', ['bar', 'baz']); + + $this->assertEquals(['foo' => 'baz'], $this->resolver->resolve()); + } + + public function testResolveFailsIfClosureReturnsFalse() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { + $passedValue = $value; + + return false; + }); + + try { + $this->resolver->resolve(); + $this->fail('Should fail'); + } catch (InvalidOptionsException $e) { + } + + $this->assertSame(42, $passedValue); + } + + public function testResolveSucceedsIfClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { + $passedValue = $value; + + return true; + }); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + $this->assertSame('bar', $passedValue); + } + + public function testResolveFailsIfAllClosuresReturnFalse() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', [ + function () { return false; }, + function () { return false; }, + function () { return false; }, + ]); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfAnyClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', [ + function () { return false; }, + function () { return true; }, + function () { return false; }, + ]); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testAddAllowedValuesFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->addAllowedValues('foo', 'bar'); + } + + public function testFailIfAddAllowedValuesFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->addAllowedValues('bar', 'baz'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testResolveFailsIfInvalidAddedValue() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedValues('foo', 'bar'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedValue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'bar'); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testResolveSucceedsIfValidAddedValueIsNull() + { + $this->resolver->setDefault('foo', null); + $this->resolver->addAllowedValues('foo', null); + + $this->assertEquals(['foo' => null], $this->resolver->resolve()); + } + + public function testResolveFailsIfInvalidAddedValueMultiple() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedValues('foo', ['bar', 'baz']); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedValueMultiple() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedValues('foo', ['bar', 'baz']); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testAddAllowedValuesDoesNotOverwrite() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'baz'); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testAddAllowedValuesDoesNotOverwrite2() + { + $this->resolver->setDefault('foo', 'baz'); + $this->resolver->setAllowedValues('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'baz'); + + $this->assertEquals(['foo' => 'baz'], $this->resolver->resolve()); + } + + public function testResolveFailsIfAllAddedClosuresReturnFalse() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', function () { return false; }); + $this->resolver->addAllowedValues('foo', function () { return false; }); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfAnyAddedClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function () { return false; }); + $this->resolver->addAllowedValues('foo', function () { return true; }); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testResolveSucceedsIfAnyAddedClosureReturnsTrue2() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function () { return true; }); + $this->resolver->addAllowedValues('foo', function () { return false; }); + + $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testSetNormalizerReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + $this->assertSame($this->resolver, $this->resolver->setNormalizer('foo', function () {})); + } + + public function testSetNormalizerClosure() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function () { + return 'normalized'; + }); + + $this->assertEquals(['foo' => 'normalized'], $this->resolver->resolve()); + } + + public function testSetNormalizerFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->setNormalizer('foo', function () {}); + } + + public function testFailIfSetNormalizerFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setNormalizer('foo', function () {}); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testNormalizerReceivesSetOption() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized['.$value.']'; + }); + + $this->assertEquals(['foo' => 'normalized[bar]'], $this->resolver->resolve()); + } + + public function testNormalizerReceivesPassedOption() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized['.$value.']'; + }); + + $resolved = $this->resolver->resolve(['foo' => 'baz']); + + $this->assertEquals(['foo' => 'normalized[baz]'], $resolved); + } + + public function testValidateTypeBeforeNormalization() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setAllowedTypes('foo', 'int'); + + $this->resolver->setNormalizer('foo', function () { + Assert::fail('Should not be called.'); + }); + + $this->resolver->resolve(); + } + + public function testValidateValueBeforeNormalization() + { + $this->expectException(InvalidOptionsException::class); + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setAllowedValues('foo', 'baz'); + + $this->resolver->setNormalizer('foo', function () { + Assert::fail('Should not be called.'); + }); + + $this->resolver->resolve(); + } + + public function testNormalizerCanAccessOtherOptions() + { + $this->resolver->setDefault('default', 'bar'); + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + /* @var TestCase $test */ + Assert::assertSame('bar', $options['default']); + + return 'normalized'; + }); + + $this->assertEquals([ + 'default' => 'bar', + 'norm' => 'normalized', + ], $this->resolver->resolve()); + } + + public function testNormalizerCanAccessLazyOptions() + { + $this->resolver->setDefault('lazy', function (Options $options) { + return 'bar'; + }); + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + /* @var TestCase $test */ + Assert::assertEquals('bar', $options['lazy']); + + return 'normalized'; + }); + + $this->assertEquals([ + 'lazy' => 'bar', + 'norm' => 'normalized', + ], $this->resolver->resolve()); + } + + public function testFailIfCyclicDependencyBetweenNormalizers() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefault('norm1', 'bar'); + $this->resolver->setDefault('norm2', 'baz'); + + $this->resolver->setNormalizer('norm1', function (Options $options) { + $options['norm2']; + }); + + $this->resolver->setNormalizer('norm2', function (Options $options) { + $options['norm1']; + }); + + $this->resolver->resolve(); + } + + public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefault('lazy', function (Options $options) { + $options['norm']; + }); + + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + $options['lazy']; + }); + + $this->resolver->resolve(); + } + + public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver() + { + $throw = true; + + $this->resolver->setDefaults(['catcher' => null, 'thrower' => null]); + + $this->resolver->setNormalizer('catcher', function (Options $options) { + try { + return $options['thrower']; + } catch (\Exception $e) { + return false; + } + }); + + $this->resolver->setNormalizer('thrower', function () use (&$throw) { + if ($throw) { + $throw = false; + throw new \UnexpectedValueException('throwing'); + } + + return true; + }); + + $this->assertSame(['catcher' => false, 'thrower' => true], $this->resolver->resolve()); + } + + public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver() + { + $throw = true; + + $this->resolver->setDefault('catcher', function (Options $options) { + try { + return $options['thrower']; + } catch (\Exception $e) { + return false; + } + }); + + $this->resolver->setDefault('thrower', function (Options $options) use (&$throw) { + if ($throw) { + $throw = false; + throw new \UnexpectedValueException('throwing'); + } + + return true; + }); + + $this->assertSame(['catcher' => false, 'thrower' => true], $this->resolver->resolve()); + } + + public function testInvokeEachNormalizerOnlyOnce() + { + $calls = 0; + + $this->resolver->setDefault('norm1', 'bar'); + $this->resolver->setDefault('norm2', 'baz'); + + $this->resolver->setNormalizer('norm1', function ($options) use (&$calls) { + Assert::assertSame(1, ++$calls); + + $options['norm2']; + }); + $this->resolver->setNormalizer('norm2', function () use (&$calls) { + Assert::assertSame(2, ++$calls); + }); + + $this->resolver->resolve(); + + $this->assertSame(2, $calls); + } + + public function testNormalizerNotCalledForUnsetOptions() + { + $this->resolver->setDefined('norm'); + + $this->resolver->setNormalizer('norm', function () { + Assert::fail('Should not be called.'); + }); + + $this->assertEmpty($this->resolver->resolve()); + } + + public function testAddNormalizerReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame($this->resolver, $this->resolver->addNormalizer('foo', function () {})); + } + + public function testAddNormalizerClosure() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return '1st-normalized-'.$value; + }); + // defined by subclass + $this->resolver->addNormalizer('foo', function (Options $options, $value) { + return '2nd-normalized-'.$value; + }); + + $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); + } + + public function testForcePrependNormalizerClosure() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return '2nd-normalized-'.$value; + }); + // defined by subclass + $this->resolver->addNormalizer('foo', function (Options $options, $value) { + return '1st-normalized-'.$value; + }, true); + + $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); + } + + public function testForcePrependNormalizerForResolverWithoutPreviousNormalizers() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addNormalizer('foo', function (Options $options, $value) { + return '1st-normalized-'.$value; + }, true); + + $this->assertEquals(['foo' => '1st-normalized-bar'], $this->resolver->resolve()); + } + + public function testAddNormalizerFailsIfUnknownOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->resolver->addNormalizer('foo', function () {}); + } + + public function testFailIfAddNormalizerFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->addNormalizer('foo', function () {}); + }); + + $this->resolver->resolve(); + } + + public function testSetDefaultsReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefaults(['foo', 'bar'])); + } + + public function testSetDefaults() + { + $this->resolver->setDefault('one', '1'); + $this->resolver->setDefault('two', 'bar'); + + $this->resolver->setDefaults([ + 'two' => '2', + 'three' => '3', + ]); + + $this->assertEquals([ + 'one' => '1', + 'two' => '2', + 'three' => '3', + ], $this->resolver->resolve()); + } + + public function testFailIfSetDefaultsFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->setDefaults(['two' => '2']); + }); + + $this->resolver->resolve(); + } + + public function testRemoveReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame($this->resolver, $this->resolver->remove('foo')); + } + + public function testRemoveSingleOption() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefault('baz', 'boo'); + $this->resolver->remove('foo'); + + $this->assertSame(['baz' => 'boo'], $this->resolver->resolve()); + } + + public function testRemoveMultipleOptions() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefault('baz', 'boo'); + $this->resolver->setDefault('doo', 'dam'); + + $this->resolver->remove(['foo', 'doo']); + + $this->assertSame(['baz' => 'boo'], $this->resolver->resolve()); + } + + public function testRemoveLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + $this->resolver->remove('foo'); + + $this->assertSame([], $this->resolver->resolve()); + } + + public function testRemoveNormalizer() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized'; + }); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testRemoveAllowedTypes() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'int'); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testRemoveAllowedValues() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', ['baz', 'boo']); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testFailIfRemoveFromLazyOption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->remove('bar'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testRemoveUnknownOptionIgnored() + { + $this->assertNotNull($this->resolver->remove('foo')); + } + + public function testClearReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->clear()); + } + + public function testClearRemovesAllOptions() + { + $this->resolver->setDefault('one', 1); + $this->resolver->setDefault('two', 2); + + $this->resolver->clear(); + + $this->assertEmpty($this->resolver->resolve()); + } + + public function testClearLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + $this->resolver->clear(); + + $this->assertSame([], $this->resolver->resolve()); + } + + public function testClearNormalizer() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized'; + }); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testClearAllowedTypes() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'int'); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testClearAllowedValues() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'baz'); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testFailIfClearFromLazyption() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', function (Options $options) { + $options->clear(); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testClearOptionAndNormalizer() + { + $this->resolver->setDefault('foo1', 'bar'); + $this->resolver->setNormalizer('foo1', function (Options $options) { + return ''; + }); + $this->resolver->setDefault('foo2', 'bar'); + $this->resolver->setNormalizer('foo2', function (Options $options) { + return ''; + }); + + $this->resolver->clear(); + $this->assertEmpty($this->resolver->resolve()); + } + + public function testArrayAccess() + { + $this->resolver->setDefault('default1', 0); + $this->resolver->setDefault('default2', 1); + $this->resolver->setRequired('required'); + $this->resolver->setDefined('defined'); + $this->resolver->setDefault('lazy1', function (Options $options) { + return 'lazy'; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) { + Assert::assertArrayHasKey('default1', $options); + Assert::assertArrayHasKey('default2', $options); + Assert::assertArrayHasKey('required', $options); + Assert::assertArrayHasKey('lazy1', $options); + Assert::assertArrayHasKey('lazy2', $options); + Assert::assertArrayNotHasKey('defined', $options); + + Assert::assertSame(0, $options['default1']); + Assert::assertSame(42, $options['default2']); + Assert::assertSame('value', $options['required']); + Assert::assertSame('lazy', $options['lazy1']); + + // Obviously $options['lazy'] and $options['defined'] cannot be + // accessed + }); + + $this->resolver->resolve(['default2' => 42, 'required' => 'value']); + } + + public function testArrayAccessGetFailsOutsideResolve() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('default', 0); + + $this->resolver['default']; + } + + public function testArrayAccessExistsFailsOutsideResolve() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('default', 0); + + isset($this->resolver['default']); + } + + public function testArrayAccessSetNotSupported() + { + $this->expectException(AccessException::class); + $this->resolver['default'] = 0; + } + + public function testArrayAccessUnsetNotSupported() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('default', 0); + + unset($this->resolver['default']); + } + + public function testFailIfGetNonExisting() + { + $this->expectException(NoSuchOptionException::class); + $this->expectExceptionMessage('The option "undefined" does not exist. Defined options are: "foo", "lazy".'); + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setDefault('lazy', function (Options $options) { + $options['undefined']; + }); + + $this->resolver->resolve(); + } + + public function testFailIfGetDefinedButUnset() + { + $this->expectException(NoSuchOptionException::class); + $this->expectExceptionMessage('The optional option "defined" has no value set. You should make sure it is set with "isset" before reading it.'); + $this->resolver->setDefined('defined'); + + $this->resolver->setDefault('lazy', function (Options $options) { + $options['defined']; + }); + + $this->resolver->resolve(); + } + + public function testFailIfCyclicDependency() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefault('lazy1', function (Options $options) { + $options['lazy2']; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) { + $options['lazy1']; + }); + + $this->resolver->resolve(); + } + + public function testCount() + { + $this->resolver->setDefault('default', 0); + $this->resolver->setRequired('required'); + $this->resolver->setDefined('defined'); + $this->resolver->setDefault('lazy1', function () {}); + + $this->resolver->setDefault('lazy2', function (Options $options) { + Assert::assertCount(4, $options); + }); + + $this->assertCount(4, $this->resolver->resolve(['required' => 'value'])); + } + + /** + * In resolve() we count the options that are actually set (which may be + * only a subset of the defined options). Outside of resolve(), it's not + * clear what is counted. + */ + public function testCountFailsOutsideResolve() + { + $this->expectException(AccessException::class); + $this->resolver->setDefault('foo', 0); + $this->resolver->setRequired('bar'); + $this->resolver->setDefined('bar'); + $this->resolver->setDefault('lazy1', function () {}); + + \count($this->resolver); + } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals([ + 'foo' => [ + [ + 1, 2, + ], + ], + ], $this->resolver->resolve([ + 'foo' => [ + [1, 2], + ], + ])); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals([ + 'foo' => [ + [ + [ + [ + 1, 2, + ], + ], + ], + ], + ], $this->resolver->resolve( + [ + 'foo' => [ + [ + [ + [1, 2], + ], + ], + ], + ] + )); + } + + public function testNestedArraysException() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "int".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve([ + 'foo' => [ + [ + [ + [1, 2], + ], + ], + ], + ]); + } + + public function testNestedArrayException1() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "bool|string|array".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve([ + 'foo' => [ + [1, true, 'str', [2, 3]], + ], + ]); + } + + public function testNestedArrayException2() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "bool|string|array".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve([ + 'foo' => [ + [true, 'str', [2, 3]], + ], + ]); + } + + public function testNestedArrayException3() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string|int".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve([ + 'foo' => [ + ['str', [1, 2]], + ], + ]); + } + + public function testNestedArrayException4() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "int".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve([ + 'foo' => [ + [ + ['str'], [1, 2], ], + ], + ]); + } + + public function testNestedArrayException5() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array".'); + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $this->resolver->resolve([ + 'foo' => [ + [ + ['str'], [1, 2], ], + ], + ]); + } + + public function testIsNestedOption() + { + $this->resolver->setDefaults([ + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefined(['host', 'port']); + }, + ]); + $this->assertTrue($this->resolver->isNested('database')); + } + + public function testFailsIfUndefinedNestedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "database[foo]" does not exist. Defined options are: "host", "port".'); + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefined(['host', 'port']); + }, + ]); + $this->resolver->resolve([ + 'database' => ['foo' => 'bar'], + ]); + } + + public function testFailsIfMissingRequiredNestedOption() + { + $this->expectException(MissingOptionsException::class); + $this->expectExceptionMessage('The required option "database[host]" is missing.'); + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver->setRequired('host'); + }, + ]); + $this->resolver->resolve([ + 'database' => [], + ]); + } + + public function testFailsIfInvalidTypeNestedOption() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "database[logging]" with value null is expected to be of type "bool", but is of type "null".'); + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver + ->setDefined('logging') + ->setAllowedTypes('logging', 'bool'); + }, + ]); + $this->resolver->resolve([ + 'database' => ['logging' => null], + ]); + } + + public function testFailsIfNotArrayIsGivenForNestedOptions() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The nested option "database" with value null is expected to be of type array, but is of type "null".'); + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefined('host'); + }, + ]); + $this->resolver->resolve([ + 'database' => null, + ]); + } + + public function testResolveNestedOptionsWithoutDefault() + { + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefined(['host', 'port']); + }, + ]); + $actualOptions = $this->resolver->resolve(); + $expectedOptions = [ + 'name' => 'default', + 'database' => [], + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testResolveNestedOptionsWithDefault() + { + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefaults([ + 'host' => 'localhost', + 'port' => 3306, + ]); + }, + ]); + $actualOptions = $this->resolver->resolve(); + $expectedOptions = [ + 'name' => 'default', + 'database' => [ + 'host' => 'localhost', + 'port' => 3306, + ], + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testResolveMultipleNestedOptions() + { + $this->resolver->setDefaults([ + 'name' => 'default', + 'database' => function (OptionsResolver $resolver) { + $resolver + ->setRequired(['dbname', 'host']) + ->setDefaults([ + 'port' => 3306, + 'replicas' => function (OptionsResolver $resolver) { + $resolver->setDefaults([ + 'host' => 'replica1', + 'port' => 3306, + ]); + }, + ]); + }, + ]); + $actualOptions = $this->resolver->resolve([ + 'name' => 'custom', + 'database' => [ + 'dbname' => 'test', + 'host' => 'localhost', + 'port' => null, + 'replicas' => ['host' => 'replica2'], + ], + ]); + $expectedOptions = [ + 'name' => 'custom', + 'database' => [ + 'port' => null, + 'replicas' => ['port' => 3306, 'host' => 'replica2'], + 'dbname' => 'test', + 'host' => 'localhost', + ], + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testResolveLazyOptionUsingNestedOption() + { + $this->resolver->setDefaults([ + 'version' => function (Options $options) { + return $options['database']['server_version']; + }, + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefault('server_version', '3.15'); + }, + ]); + $actualOptions = $this->resolver->resolve(); + $expectedOptions = [ + 'database' => ['server_version' => '3.15'], + 'version' => '3.15', + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testNormalizeNestedOptionValue() + { + $this->resolver + ->setDefaults([ + 'database' => function (OptionsResolver $resolver) { + $resolver->setDefaults([ + 'port' => 3306, + 'host' => 'localhost', + 'dbname' => 'demo', + ]); + }, + ]) + ->setNormalizer('database', function (Options $options, $value) { + ksort($value); + + return $value; + }); + $actualOptions = $this->resolver->resolve([ + 'database' => ['dbname' => 'test'], + ]); + $expectedOptions = [ + 'database' => ['dbname' => 'test', 'host' => 'localhost', 'port' => 3306], + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testOverwrittenNestedOptionNotEvaluatedIfLazyDefault() + { + // defined by superclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + Assert::fail('Should not be called'); + }); + // defined by subclass + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + $this->assertSame(['foo' => 'lazy'], $this->resolver->resolve()); + } + + public function testOverwrittenNestedOptionNotEvaluatedIfScalarDefault() + { + // defined by superclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + Assert::fail('Should not be called'); + }); + // defined by subclass + $this->resolver->setDefault('foo', 'bar'); + $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); + } + + public function testOverwrittenLazyOptionNotEvaluatedIfNestedOption() + { + // defined by superclass + $this->resolver->setDefault('foo', function (Options $options) { + Assert::fail('Should not be called'); + }); + // defined by subclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + $resolver->setDefault('bar', 'baz'); + }); + $this->assertSame(['foo' => ['bar' => 'baz']], $this->resolver->resolve()); + } + + public function testResolveAllNestedOptionDefinitions() + { + // defined by superclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + $resolver->setRequired('bar'); + }); + // defined by subclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + $resolver->setDefault('bar', 'baz'); + }); + // defined by subclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + $resolver->setDefault('ping', 'pong'); + }); + $this->assertSame(['foo' => ['ping' => 'pong', 'bar' => 'baz']], $this->resolver->resolve()); + } + + public function testNormalizeNestedValue() + { + // defined by superclass + $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { + $resolver->setDefault('bar', null); + }); + // defined by subclass + $this->resolver->setNormalizer('foo', function (Options $options, $resolvedValue) { + if (null === $resolvedValue['bar']) { + $resolvedValue['bar'] = 'baz'; + } + + return $resolvedValue; + }); + $this->assertSame(['foo' => ['bar' => 'baz']], $this->resolver->resolve()); + } + + public function testFailsIfCyclicDependencyBetweenSameNestedOption() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('replicas', $parent['database']); + }); + $this->resolver->resolve(); + } + + public function testFailsIfCyclicDependencyBetweenNestedOptionAndParentLazyOption() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefaults([ + 'version' => function (Options $options) { + return $options['database']['server_version']; + }, + 'database' => function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('server_version', $parent['version']); + }, + ]); + $this->resolver->resolve(); + } + + public function testFailsIfCyclicDependencyBetweenNormalizerAndNestedOption() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver + ->setDefault('name', 'default') + ->setDefault('database', function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('host', $parent['name']); + }) + ->setNormalizer('name', function (Options $options, $value) { + $options['database']; + }); + $this->resolver->resolve(); + } + + public function testFailsIfCyclicDependencyBetweenNestedOptions() + { + $this->expectException(OptionDefinitionException::class); + $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('host', $parent['replica']['host']); + }); + $this->resolver->setDefault('replica', function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('host', $parent['database']['host']); + }); + $this->resolver->resolve(); + } + + public function testGetAccessToParentOptionFromNestedOption() + { + $this->resolver->setDefaults([ + 'version' => 3.15, + 'database' => function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('server_version', $parent['version']); + }, + ]); + $this->assertSame(['version' => 3.15, 'database' => ['server_version' => 3.15]], $this->resolver->resolve()); + } + + public function testNestedClosureWithoutTypeHintNotInvoked() + { + $closure = function ($resolver) { + Assert::fail('Should not be called'); + }; + $this->resolver->setDefault('foo', $closure); + $this->assertSame(['foo' => $closure], $this->resolver->resolve()); + } + + public function testNestedClosureWithoutTypeHint2ndArgumentNotInvoked() + { + $closure = function (OptionsResolver $resolver, $parent) { + Assert::fail('Should not be called'); + }; + $this->resolver->setDefault('foo', $closure); + $this->assertSame(['foo' => $closure], $this->resolver->resolve()); + } + + public function testResolveLazyOptionWithTransitiveDefaultDependency() + { + $this->resolver->setDefaults([ + 'ip' => null, + 'database' => function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('host', $parent['ip']); + $resolver->setDefault('primary_replica', function (OptionsResolver $resolver, Options $parent) { + $resolver->setDefault('host', $parent['host']); + }); + }, + 'secondary_replica' => function (Options $options) { + return $options['database']['primary_replica']['host']; + }, + ]); + $actualOptions = $this->resolver->resolve(['ip' => '127.0.0.1']); + $expectedOptions = [ + 'ip' => '127.0.0.1', + 'database' => [ + 'host' => '127.0.0.1', + 'primary_replica' => ['host' => '127.0.0.1'], + ], + 'secondary_replica' => '127.0.0.1', + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testAccessToParentOptionFromNestedNormalizerAndLazyOption() + { + $this->resolver->setDefaults([ + 'debug' => true, + 'database' => function (OptionsResolver $resolver, Options $parent) { + $resolver + ->setDefined('logging') + ->setDefault('profiling', function (Options $options) use ($parent) { + return $parent['debug']; + }) + ->setNormalizer('logging', function (Options $options, $value) use ($parent) { + return false === $parent['debug'] ? true : $value; + }); + }, + ]); + $actualOptions = $this->resolver->resolve([ + 'debug' => false, + 'database' => ['logging' => false], + ]); + $expectedOptions = [ + 'debug' => false, + 'database' => ['profiling' => false, 'logging' => true], + ]; + $this->assertSame($expectedOptions, $actualOptions); + } + + public function testFailsIfOptionIsAlreadyDefined() + { + $this->expectException(OptionDefinitionException::class); + $this->expectExceptionMessage('The option "foo" is already defined.'); + $this->resolver->define('foo'); + $this->resolver->define('foo'); + } + + public function testResolveOptionsDefinedByOptionConfigurator() + { + $this->resolver->define('foo') + ->required() + ->deprecated('vendor/package', '1.1') + ->default('bar') + ->allowedTypes('string', 'bool') + ->allowedValues('bar', 'zab') + ->normalize(static function (Options $options, $value) { + return $value; + }) + ->info('info message') + ; + $introspector = new OptionsResolverIntrospector($this->resolver); + + $this->assertTrue(true, $this->resolver->isDefined('foo')); + $this->assertTrue(true, $this->resolver->isDeprecated('foo')); + $this->assertTrue(true, $this->resolver->hasDefault('foo')); + $this->assertSame('bar', $introspector->getDefault('foo')); + $this->assertSame(['string', 'bool'], $introspector->getAllowedTypes('foo')); + $this->assertSame(['bar', 'zab'], $introspector->getAllowedValues('foo')); + $this->assertCount(1, $introspector->getNormalizers('foo')); + $this->assertSame('info message', $this->resolver->getInfo('foo')); + } + + public function testGetInfo() + { + $info = 'The option info message'; + $this->resolver->setDefined('foo'); + $this->resolver->setInfo('foo', $info); + + $this->assertSame($info, $this->resolver->getInfo('foo')); + } + + public function testSetInfoOnNormalization() + { + $this->expectException(AccessException::class); + $this->expectExceptionMessage('The Info message cannot be set from a lazy option or normalizer.'); + + $this->resolver->setDefined('foo'); + $this->resolver->setNormalizer('foo', static function (Options $options, $value) { + $options->setInfo('foo', 'Info'); + }); + + $this->resolver->resolve(['foo' => 'bar']); + } + + public function testSetInfoOnUndefinedOption() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "bar" does not exist. Defined options are: "foo".'); + + $this->resolver->setDefined('foo'); + $this->resolver->setInfo('bar', 'The option info message'); + } + + public function testGetInfoOnUndefinedOption2() + { + $this->expectException(UndefinedOptionsException::class); + $this->expectExceptionMessage('The option "bar" does not exist. Defined options are: "foo".'); + + $this->resolver->setDefined('foo'); + $this->resolver->getInfo('bar'); + } + + public function testInfoOnInvalidValue() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The option "expires" with value DateTime is invalid. Info: A future date time.'); + + $this->resolver + ->setRequired('expires') + ->setInfo('expires', 'A future date time') + ->setAllowedTypes('expires', \DateTime::class) + ->setAllowedValues('expires', static function ($value) { + return $value >= new \DateTime('now'); + }) + ; + + $this->resolver->resolve(['expires' => new \DateTime('-1 hour')]); + } + + /** + * @group legacy + */ + public function testSetDeprecatedWithoutPackageAndVersion() + { + $this->expectDeprecation('Since symfony/options-resolver 5.1: The signature of method "Symfony\Component\OptionsResolver\OptionsResolver::setDeprecated()" requires 2 new arguments: "string $package, string $version", not defining them is deprecated.'); + + $this->resolver + ->setDefined('foo') + ->setDeprecated('foo') + ; + } + + public function testInvalidValueForPrototypeDefinition() + { + $this->expectException(InvalidOptionsException::class); + $this->expectExceptionMessage('The value of the option "connections" is expected to be of type array of array, but is of type array of "string".'); + + $this->resolver + ->setDefault('connections', static function (OptionsResolver $resolver) { + $resolver + ->setPrototype(true) + ->setDefined(['table', 'user', 'password']) + ; + }) + ; + + $this->resolver->resolve(['connections' => ['foo']]); + } + + public function testMissingOptionForPrototypeDefinition() + { + $this->expectException(MissingOptionsException::class); + $this->expectExceptionMessage('The required option "connections[1][table]" is missing.'); + + $this->resolver + ->setDefault('connections', static function (OptionsResolver $resolver) { + $resolver + ->setPrototype(true) + ->setRequired('table') + ; + }) + ; + + $this->resolver->resolve(['connections' => [ + ['table' => 'default'], + [], // <- missing required option "table" + ]]); + } + + public function testAccessExceptionOnPrototypeDefinition() + { + $this->expectException(AccessException::class); + $this->expectExceptionMessage('The prototype property cannot be set from a root definition.'); + + $this->resolver->setPrototype(true); + } + + public function testPrototypeDefinition() + { + $this->resolver + ->setDefault('connections', static function (OptionsResolver $resolver) { + $resolver + ->setPrototype(true) + ->setRequired('table') + ->setDefaults(['user' => 'root', 'password' => null]) + ; + }) + ; + + $actualOptions = $this->resolver->resolve([ + 'connections' => [ + 'default' => [ + 'table' => 'default', + ], + 'custom' => [ + 'user' => 'foo', + 'password' => 'pa$$', + 'table' => 'symfony', + ], + ], + ]); + $expectedOptions = [ + 'connections' => [ + 'default' => [ + 'user' => 'root', + 'password' => null, + 'table' => 'default', + ], + 'custom' => [ + 'user' => 'foo', + 'password' => 'pa$$', + 'table' => 'symfony', + ], + ], + ]; + + $this->assertSame($expectedOptions, $actualOptions); + } +} diff --git a/vendor/symfony/options-resolver/phpunit.xml.dist b/vendor/symfony/options-resolver/phpunit.xml.dist new file mode 100644 index 000000000..9a2ec111c --- /dev/null +++ b/vendor/symfony/options-resolver/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/polyfill-ctype/composer.json b/vendor/symfony/polyfill-ctype/composer.json index 995978c0a..f0621a3b6 100644 --- a/vendor/symfony/polyfill-ctype/composer.json +++ b/vendor/symfony/polyfill-ctype/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", diff --git a/vendor/symfony/polyfill-php73/composer.json b/vendor/symfony/polyfill-php73/composer.json index 8b99ab890..a7fe47875 100644 --- a/vendor/symfony/polyfill-php73/composer.json +++ b/vendor/symfony/polyfill-php73/composer.json @@ -26,7 +26,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", diff --git a/vendor/symfony/polyfill-php80/composer.json b/vendor/symfony/polyfill-php80/composer.json index a9e6813c9..5fe679db3 100644 --- a/vendor/symfony/polyfill-php80/composer.json +++ b/vendor/symfony/polyfill-php80/composer.json @@ -30,7 +30,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-main": "1.22-dev" + "dev-main": "1.23-dev" }, "thanks": { "name": "symfony/polyfill", diff --git a/vendor/symfony/process/README.md b/vendor/symfony/process/README.md index b7ca5b425..afce5e45e 100644 --- a/vendor/symfony/process/README.md +++ b/vendor/symfony/process/README.md @@ -6,8 +6,8 @@ The Process component executes commands in sub-processes. Resources --------- - * [Documentation](https://symfony.com/doc/current/components/process.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) + * [Documentation](https://symfony.com/doc/current/components/process.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony)